跳到主要内容用 Python 生成 Node.js 项目结构的桌面工具 | 极客日志PythonNode.js大前端
用 Python 生成 Node.js 项目结构的桌面工具
这是一个用 Python 和 wxPython 编写的桌面工具,用来把树形文本快速落成 Node.js 项目目录,同时提供目录浏览、文件内容读写、备注管理和配置持久化。核心做法是先解析带缩进的结构文本,再用路径栈创建文件夹和空文件;目录展示侧重避免深层递归和无谓刷新,文件读取则用 UTF-8 和 GBK 双编码兜底。整体思路不复杂,但很实用,适合项目初始化和本地文件管理。
项目概述
这个工具的目标很直接:把一段树形文本变成实际的 Node.js 项目目录,并顺手提供文件浏览、内容编辑和备注管理。它不是那种'什么都想做一点'的大而全工具,重点还是放在项目初始化和文件管理这两个场景上。
功能特性
核心能力
- 文件夹选择和浏览
- 根据文本描述批量创建文件夹和文件
- 树形展示目录层级
- 查看和编辑文件内容
- 备注列表和剪贴板操作
- 自动保存并恢复配置
技术架构
技术栈
- Python 3.x
- wxPython:GUI 框架
- 标准库:
os、re、json、subprocess、platform
程序结构
FileManagerFrame(主窗口类)
├── 界面初始化
├── 事件处理方法
├── 文件操作方法
└── 配置管理方法
代码说明
1. 类初始化与界面布局
class FileManagerFrame(wx.Frame):
def __init__(self):
super().__init__(parent=None, title='文件管理器', size=(1200, 800))
界面用了双面板布局,左边放文件夹操作、结构输入、树形展示和备注列表,右边是文件内容编辑区。这个拆法挺实用,目录管理和内容编辑分开后,操作路径会清楚很多。
主要组件大致如下:
self.folder_text
self.memo1
self.tree_ctrl
self.memo2
self.listbox1
self.edit1
2. 文件夹选择功能
def on_select_folder(self, event):
dlg = wx.DirDialog(, , style=wx.DD_DEFAULT_STYLE)
dlg.ShowModal() == wx.ID_OK:
.target_folder = dlg.GetPath()
.folder_text.SetValue(.target_folder)
dlg.Destroy()
self
"选择目标文件夹"
if
self
self
self
这里直接用了 wx.DirDialog,体验上比自己拼一个路径输入框稳一点。选完文件夹后没有立刻刷新树,这个处理我觉得是对的,目录大一点时,避免一上来就卡死界面。
3. 文件夹结构解析算法
这是核心逻辑之一,负责把树形文本解析成可创建的目录结构。
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
structure.append({'name': name, 'is_folder': is_folder, 'level': level})
return structure, root_name
- 用正则去掉行首的树形字符
- 通过
( 截掉后面的注释
- 以
/ 结尾判断文件夹
- 按缩进深度算层级
实现不复杂,但够用。约定每 4 个字符算一个层级,这种方案对输入格式有要求,换成别的树形风格就未必稳了,不过在自己可控的工具里,这个取舍很正常。
4. 文件系统创建算法
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]
5. 树形控件填充
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 是个比较保守的限制,能挡住很多没必要的深层递归;如果目录树特别大,真正的瓶颈也往往不在这一层。
6. 文件内容读写
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:
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)
读取时先试 UTF-8,再试 GBK,这个顺序很常见,也比较符合实际环境。Windows 老项目里碰到 GBK 文件并不稀奇,直接只认 UTF-8 的编辑器经常会吃亏。
7. 配置持久化
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))
保存的是两个很朴素的状态:目标文件夹和备注列表。没有做复杂的状态机,也没必要。用 JSON 做持久化足够了,ensure_ascii=False 也避免了中文被转义成一串 \uXXXX。
8. 系统集成功能
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':
subprocess.Popen(['open', self.target_folder])
else:
subprocess.Popen(['xdg-open', self.target_folder])
except Exception as e:
wx.MessageBox('打开文件夹失败: ' + str(e), '错误', wx.OK | wx.ICON_ERROR)
这段就是典型的跨平台打开目录处理。三套命令分别对应 Windows、macOS 和 Linux,写法不花哨,但够稳。
使用场景
项目模板生成
my-project/
├── src/
│ ├── main.js
│ └── utils.js
├── public/
│ └── index.html
├── package.json
└── README.md
批量文件管理
- 浏览大型项目目录
- 快速定位和编辑文件
- 不退出程序就能查看文件内容
文档整理
性能处理
延迟加载
wx.CallAfter(self.refresh_tree)
控制递归深度
过滤系统文件
if item[0] == '.' or item[0] == '$':
continue
可以继续补的地方
- 文件搜索
- 拖拽导入
- 多文件批量编辑
- 图片、PDF 预览
- 文件对比
- 快捷键
- 主题切换
- 最近打开列表
- 书签
- 操作历史
- Git 集成
- 正则搜索
- 代码高亮
- 插件系统
常见问题
Q1: 中文乱码问题
处理方式是多编码尝试,先 UTF-8,失败后再试 GBK。
Q2: 大文件夹加载慢
可以先不自动刷新,配合深度限制和隐藏文件过滤一起用,效果会比单纯优化界面更明显。
Q3: 树节点展开报错
如果启用了 wx.TR_HIDE_ROOT,展开时容易出问题,直接去掉这个样式,让根节点可见会省事很多。
Q4: 配置文件损坏
加载配置时用 try-except 包一层,失败就回退默认值,不要让配置文件把整个程序拖死。
相关免费在线工具
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
- Markdown转HTML
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
- HTML转Markdown
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
- JSON 压缩
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online