Python 开发 Node.js 项目结构生成工具详解
使用 Python 和 wxPython 开发的一个 Node.js 项目结构生成工具。该工具支持文件夹结构可视化创建、树形目录浏览、文件内容编辑及配置持久化等功能,适用于快速搭建标准化项目模板和文件管理任务。

使用 Python 和 wxPython 开发的一个 Node.js 项目结构生成工具。该工具支持文件夹结构可视化创建、树形目录浏览、文件内容编辑及配置持久化等功能,适用于快速搭建标准化项目模板和文件管理任务。

本文介绍一个用于快速生成 Node.js 项目文件结构的 Python 工具。该工具具备文件夹结构创建、浏览、文件内容查看与编辑等功能,适用于文件管理或项目初始化场景。
FileManagerFrame (主窗口类)
├── 界面初始化
├── 事件处理方法
├── 文件操作方法
└── 配置管理方法
class FileManagerFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title='文件管理器', size=(1200, 800))
设计思路:
关键组件:
# 主要的UI组件
self.folder_text # 显示目标文件夹路径
self.memo1 # 输入文件夹结构
self.tree_ctrl # 树形控件,展示目录结构
self.memo2 # 文件内容编辑器
self.listbox1 # 备注列表
self.edit1 # 备注输入框
def on_select_folder(self, event):
dlg = wx.DirDialog(self, "选择目标文件夹", style=wx.DD_DEFAULT_STYLE)
if dlg.ShowModal() == wx.ID_OK:
self.target_folder = dlg.GetPath()
self.folder_text.SetValue(self.target_folder)
dlg.Destroy()
技术要点:
wx.DirDialog 提供原生的文件夹选择对话框dlg.Destroy() 手动释放对话框资源这是程序的核心算法之一,用于解析树形文本结构:
def parse_folder_structure(self, structure_text):
lines = structure_text.strip().split('\n')
structure = []
root_name = None
for i, line in enumerate(lines):
# 移除树形字符 (│├└─)
clean_line = re.sub(r'^[│├└─\s]+', '', line).strip()
# 提取名称(去掉注释)
name = clean_line.split('(')[0].strip()
is_folder = name.endswith('/')
name = name.rstrip('/')
if i == 0:
root_name = name
structure.append({'name': name, 'is_folder': True, 'level': 0})
else:
# 计算层级(通过树形字符数量)
tree_chars = len(line) - len(line.lstrip('│├└─ \t'))
level = tree_chars // 4 + 1 # 每4个字符一个层级
structure.append({'name': name, 'is_folder': is_folder, 'level': level})
return structure, root_name
算法解析:
r'^[│├└─\s]+' 匹配行首的树形字符├── file.txt 的层级为 1/ 结尾的视为文件夹server.js (后端入口)def create_from_structure(self, structure, base_path):
path_stack = [base_path] # 使用栈维护当前路径
for i, item in enumerate(structure):
name = item['name']
is_folder = item['is_folder']
level = item['level']
if i == 0:
continue # 跳过根节点
# 根据层级调整路径栈
while len(path_stack) > level:
path_stack.pop()
# 构建当前路径
current_path = os.path.join(path_stack[-1], name)
if is_folder:
os.makedirs(current_path, exist_ok=True)
path_stack.append(current_path) # 文件夹入栈
else:
# 创建文件
parent_dir = os.path.dirname(current_path)
if parent_dir:
os.makedirs(parent_dir, exist_ok=True)
with open(current_path, 'w', encoding='utf-8') as f:
pass # 创建空文件
数据结构:路径栈
路径栈是这个算法的关键:
示例:
my-project/ level=0 栈:[base_path]
├── src/ level=1 栈:[base_path, src]
│ └── main.js level=2 栈:[base_path, src]
└── public/ level=1 栈:[base_path, public] (src出栈)
└── index.html level=2 栈:[base_path, public]
def populate_tree(self, parent_item, path, depth=0, max_depth=10):
if depth >= max_depth:
return # 防止递归过深
try:
items = sorted(os.listdir(path))
folders = []
files = []
# 分类文件和文件夹
for item in items:
if len(item) > 0 and item[0] == '.':
continue # 跳过隐藏文件
if len(item) > 0 and item[0] == '$':
continue # 跳过系统文件
item_path = os.path.join(path, item)
try:
if os.path.isdir(item_path):
folders.append((item, item_path))
else:
files.append((item, item_path))
except (PermissionError, OSError):
continue
# 先显示文件夹
for item_name, item_path in folders:
display_name = '[DIR] ' + item_name
child = self.tree_ctrl.AppendItem(parent_item, display_name)
self.tree_ctrl.SetItemData(child, item_path)
self.populate_tree(child, item_path, depth + 1, max_depth)
# 再显示文件
for item_name, item_path in files:
display_name = '[FILE] ' + item_name
child = self.tree_ctrl.AppendItem(parent_item, display_name)
self.tree_ctrl.SetItemData(child, item_path)
except (PermissionError, OSError):
pass
设计亮点:
max_depth=10 防止目录层级过深导致性能问题SetItemData 将完整路径与树节点关联def load_file_content(self, file_path):
"""加载文件内容 - 支持多种编码"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
self.memo2.SetValue(content)
except Exception:
try:
# 尝试GBK编码(兼容中文Windows)
with open(file_path, 'r', encoding='gbk') as f:
content = f.read()
self.memo2.SetValue(content)
except Exception as e:
self.memo2.SetValue('无法读取文件内容: ' + str(e))
def on_save_file(self, event):
"""保存文件内容"""
if not self.current_file:
wx.MessageBox('请先选择一个文件', '提示', wx.OK | wx.ICON_INFORMATION)
return
try:
content = self.memo2.GetValue()
with open(self.current_file, 'w', encoding='utf-8') as f:
f.write(content)
wx.MessageBox('保存成功!', '提示', wx.OK | wx.ICON_INFORMATION)
except Exception as e:
wx.MessageBox('保存失败: ' + str(e), '错误', wx.OK | wx.ICON_ERROR)
编码兼容性处理:
def save_config(self):
"""保存配置到JSON文件"""
config = {
'target_folder': self.target_folder,
'listbox_items': [self.listbox1.GetString(i) for i in range(self.listbox1.GetCount())]
}
try:
with open(self.config_file, 'w', encoding='utf-8') as f:
json.dump(config, f, ensure_ascii=False, indent=2)
except Exception as e:
print('保存配置失败: ' + str(e))
def load_config(self):
"""程序启动时加载配置"""
if os.path.exists(self.config_file):
try:
with open(self.config_file, 'r', encoding='utf-8') as f:
config = json.load(f)
self.target_folder = config.get('target_folder', '')
self.folder_text.SetValue(self.target_folder)
listbox_items = config.get('listbox_items', [])
for item in listbox_items:
self.listbox1.Append(item)
except Exception as e:
print('加载配置失败: ' + str(e))
配置项:
优势:
ensure_ascii=False 保证中文正确保存def on_open_folder(self, event):
"""调用系统文件管理器打开文件夹"""
if not self.target_folder or not os.path.exists(self.target_folder):
wx.MessageBox('请先选择有效的目标文件夹', '提示', wx.OK | wx.ICON_INFORMATION)
return
try:
if platform.system() == 'Windows':
os.startfile(self.target_folder)
elif platform.system() == 'Darwin': # macOS
subprocess.Popen(['open', self.target_folder])
else: # Linux
subprocess.Popen(['xdg-open', self.target_folder])
except Exception as e:
wx.MessageBox('打开文件夹失败: ' + str(e), '错误', wx.OK | wx.ICON_ERROR)
跨平台兼容性:
os.startfile()open 命令xdg-open 命令快速创建标准项目结构:
my-project/
├── src/
│ ├── main.js
│ └── utils.js
├── public/
│ └── index.html
├── package.json
└── README.md
wx.CallAfter(self.refresh_tree) # 异步刷新,避免阻塞
max_depth=10 # 限制递归深度
if item[0] == '.' or item[0] == '$':
continue # 跳过不必要的文件
解决:使用多编码尝试机制,优先UTF-8,备选GBK
解决:
解决:移除 wx.TR_HIDE_ROOT 样式,显示根节点
解决:使用 try-except 包裹配置加载,失败时使用默认值

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online