From a4371e2c516fb4955c5f608dcf0e70a47bd6b403 Mon Sep 17 00:00:00 2001 From: Rick Anderson Date: Wed, 12 Aug 2026 20:53:21 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E5=B0=86=E5=B8=B8=E9=87=8F?= =?UTF-8?q?=E7=9A=84=E8=8E=B7=E5=8F=96=E6=A8=A1=E5=9D=97=E5=8C=96=EF=BC=8C?= =?UTF-8?q?=E5=B9=B6=E4=B8=BA=E5=88=B6=E4=BD=9C=E5=8F=AF=E6=89=A7=E8=A1=8C?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=81=9A=E4=B8=80=E4=BA=9B=E5=BF=85=E8=A6=81?= =?UTF-8?q?=E7=9A=84=E4=BF=AE=E6=94=B9=20-=20=E5=B0=86=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E5=92=8C=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E5=B8=B8=E9=87=8F=E7=9A=84=E8=8E=B7=E5=8F=96=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=E6=94=BE=E5=9C=A8=20paths.py=20=E6=A8=A1=E5=9D=97=E4=B8=AD=20-?= =?UTF-8?q?=20=E5=B0=86=E9=BB=98=E8=AE=A4=E5=88=86=E7=B1=BB=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E6=94=BE=E5=9C=A8=20categories.py=20=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E4=B8=AD=20-=20=E6=97=A5=E5=BF=97=E5=92=8C=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E7=9B=AE=E5=BD=95=E5=B8=B8=E9=87=8F?= =?UTF-8?q?=E7=9A=84=E8=8E=B7=E5=8F=96=E7=8E=B0=E5=9C=A8=E5=85=BC=E5=AE=B9?= =?UTF-8?q?=E4=BA=86=20=E5=8F=AF=E6=89=A7=E8=A1=8C=E6=96=87=E4=BB=B6=20?= =?UTF-8?q?=E7=9A=84=E6=93=8D=E4=BD=9C=EF=BC=88=E6=AD=A4=E5=89=8D=E5=8F=AA?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E7=9B=B4=E6=8E=A5=E8=BF=90=E8=A1=8C=20Python?= =?UTF-8?q?=20=E6=96=87=E4=BB=B6=EF=BC=89=20-=20=E6=89=A9=E5=85=85?= =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=BA=86=E9=BB=98=E8=AE=A4=E5=88=86=E7=B1=BB?= =?UTF-8?q?=E6=96=B9=E6=B3=95=EF=BC=88=E7=8E=B0=E5=9C=A8=E6=B6=B5=E7=9B=96?= =?UTF-8?q?=E4=BA=86=E5=A4=A7=E9=83=A8=E5=88=86=E5=B8=B8=E7=94=A8=E7=9A=84?= =?UTF-8?q?=E5=90=8E=E7=BC=80=E5=90=8D=E5=92=8C=E5=AF=B9=E5=BA=94=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- categories.py | 33 +++++++++++++++++++++++++++++++++ move_items.py | 14 +++----------- paths.py | 10 ++++++++++ undo.py | 4 +--- "\350\256\276\350\256\241.md" | 8 ++++++++ 5 files changed, 55 insertions(+), 14 deletions(-) create mode 100644 categories.py create mode 100644 paths.py diff --git a/categories.py b/categories.py new file mode 100644 index 0000000..046cce4 --- /dev/null +++ b/categories.py @@ -0,0 +1,33 @@ +DEFAULT_CATEGORIES = { + # 图片 + ".jpg": "图片", ".jpeg": "图片", ".png": "图片", ".gif": "图片", + ".bmp": "图片", ".svg": "图片", ".webp": "图片", ".ico": "图片", + ".tiff": "图片", ".heic": "图片", + # 文档 + ".txt": "文档", ".md": "文档", ".pdf": "文档", ".doc": "文档", + ".docx": "文档", ".rtf": "文档", ".odt": "文档", ".epub": "文档", + # 表格与数据 + ".xls": "表格", ".xlsx": "表格", ".csv": "表格", + # 演示文稿 + ".ppt": "演示文稿", ".pptx": "演示文稿", + # 视频 + ".mp4": "视频", ".mkv": "视频", ".avi": "视频", ".mov": "视频", + ".wmv": "视频", ".flv": "视频", ".webm": "视频", ".m4v": "视频", + ".mpg": "视频", ".mpeg": "视频", + # 音频 + ".mp3": "音频", ".wav": "音频", ".flac": "音频", ".aac": "音频", + ".ogg": "音频", ".m4a": "音频", ".wma": "音频", + # 压缩包 + ".zip": "压缩包", ".rar": "压缩包", ".7z": "压缩包", ".tar": "压缩包", + ".gz": "压缩包", ".bz2": "压缩包", ".xz": "压缩包", ".iso": "压缩包", + # 代码 + ".py": "代码", ".js": "代码", ".ts": "代码", ".html": "代码", + ".css": "代码", ".c": "代码", ".cpp": "代码", ".h": "代码", + ".java": "代码", ".go": "代码", ".rs": "代码", ".json": "代码", + ".xml": "代码", ".yaml": "代码", ".yml": "代码", ".toml": "代码", + ".sh": "代码", ".bat": "代码", ".ps1": "代码", + # 安装包 + ".exe": "安装包", ".msi": "安装包", ".apk": "安装包", ".dmg": "安装包", + # 字体 + ".ttf": "字体", ".otf": "字体", ".woff": "字体", ".woff2": "字体", +} \ No newline at end of file diff --git a/move_items.py b/move_items.py index 9baf39e..b565bf8 100644 --- a/move_items.py +++ b/move_items.py @@ -2,16 +2,8 @@ import shutil from datetime import datetime import json - -LOG_FILE = Path(__file__).parent / "log.jsonl" - -# 扩展名和目标文件夹名的对应关系: -DEFAULT_CATEGORIES = { - ".jpg": "图片", ".png": "图片", - ".txt": "文档", ".md": "文档", ".pdf": "文档", ".docx": "文档", - ".mp4": "视频", ".mkv": "视频", -} -CONFIG_FILE = LOG_FILE.parent / "config.json" +from paths import LOG_FILE, CONFIG_FILE +from categories import DEFAULT_CATEGORIES def load_categories() -> dict: """读取 config.json 的分类规则:缺失时按默认值创建,损坏时回退到内置默认值""" @@ -55,7 +47,7 @@ def plan(src: Path) -> list: return moves def confirm_prompt(moves: list) -> bool: - """计算出所有移动关系,输出,确认是否要执行""" + """计算出所有移动关系,输出,确认是否执行""" print(f"将移动 {len(moves)} 个文件:") for item, target, cat in moves: print(f" {item.name} -> {cat} / {target.name}") diff --git a/paths.py b/paths.py new file mode 100644 index 0000000..7b0574d --- /dev/null +++ b/paths.py @@ -0,0 +1,10 @@ +from pathlib import Path +import sys + +if getattr(sys, "frozen", False): + BASE_DIR = Path(sys.executable).parent +else: + BASE_DIR = Path(__file__).parent + +LOG_FILE = BASE_DIR / "log.jsonl" +CONFIG_FILE = BASE_DIR / "config.json" \ No newline at end of file diff --git a/undo.py b/undo.py index 74aa47a..838f99f 100644 --- a/undo.py +++ b/undo.py @@ -1,9 +1,7 @@ from pathlib import Path import shutil import json - -LOG_FILE = Path(__file__).parent / "log.jsonl" # 文件路径 - +from paths import LOG_FILE def read_log() -> list: records = [] diff --git "a/\350\256\276\350\256\241.md" "b/\350\256\276\350\256\241.md" index 1eccb63..469f2db 100644 --- "a/\350\256\276\350\256\241.md" +++ "b/\350\256\276\350\256\241.md" @@ -181,3 +181,11 @@ def write_log(item: Path, target: Path, ts: str): 之前的分类依据是直接写进代码的,这样在打包成可执行文件之后没法改了。这里把配置文件单独存在一个 `config.json` 文件中。 需要注意,当不存在配置文件的时候,要按照默认值创建一个;当文件损坏时,要使用默认值(但不会直接修改文件)。 + +#### (2)将文件夹路径和分类依据模块化 + +文件夹的目录比较复杂,要分 py 文件和可执行文件两种情况处理(**注意这种情况是首次进行处理,之前的上传都没有为可执行文件做准备**);并且不同的功能模块都要用到它(比如目前的移动和撤回功能都要找到 log.jsonl 的路径)。为了遵循不重复原则,这里将文件夹路径、日志路径、配置文件路径三个常量都放在了 `path.py` 文件中。 + +分类依据则不相同,目前并不违背不重复原则,但是完善的默认分类依据实在是太长了(80 行),不太适合直接放进 move_items.py 文件中,因此这里也将其拆到了单独的 `categories.py` 文件中。 + +以上二者都通过 `from ... import ...` 语句访问。 From fb3e100d3aba3861f48a73be4a80441793718552 Mon Sep 17 00:00:00 2001 From: Rick Anderson Date: Thu, 13 Aug 2026 14:40:31 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E4=BA=86?= =?UTF-8?q?=E4=B8=BB=E6=A8=A1=E5=9D=97=EF=BC=8C=E6=94=AF=E6=8C=81=E7=94=A8?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E8=A1=8C=E5=8F=82=E6=95=B0=E6=93=8D=E6=8E=A7?= =?UTF-8?q?=20-=20=E4=B8=BB=E6=A8=A1=E5=9D=97=E7=9A=84=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E4=B8=BA=20file=5Fmanager.py=20-=20=E4=B8=BB=E6=A8=A1=E5=9D=97?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=91=BD=E4=BB=A4=E8=A1=8C=E5=8F=82=E6=95=B0?= =?UTF-8?q?=EF=BC=8C=E5=8F=AF=E7=94=A8=20-h=20/=20--help=20=E6=9F=A5?= =?UTF-8?q?=E7=9C=8B=20-=20=E5=91=BD=E4=BB=A4=E8=A1=8C=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=9C=89=E4=B8=A4=E4=B8=AA=E5=AD=90=E5=91=BD=E4=BB=A4=EF=BC=9A?= =?UTF-8?q?move=20=E5=92=8C=20undo=EF=BC=8C=E5=AF=B9=E5=BA=94=E7=A7=BB?= =?UTF-8?q?=E5=8A=A8=E5=92=8C=E6=92=A4=E9=94=80=E6=93=8D=E4=BD=9C=20-=20mo?= =?UTF-8?q?ve=20=E5=91=BD=E4=BB=A4=E6=9C=89=E4=B8=80=E4=B8=AA=E5=BC=BA?= =?UTF-8?q?=E5=88=B6=E9=80=89=E9=A1=B9=E5=8F=82=E6=95=B0=20--source?= =?UTF-8?q?=EF=BC=8C=E6=98=AF=E8=A6=81=E8=BF=9B=E8=A1=8C=E6=95=B4=E7=90=86?= =?UTF-8?q?=E7=9A=84=E6=96=87=E4=BB=B6=E5=A4=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 4 +++- file_manager.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 file_manager.py diff --git a/.gitignore b/.gitignore index fe00f72..49c7c53 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ log.jsonl -config.json \ No newline at end of file +config.json +__pycache__/ +*.py[cod] \ No newline at end of file diff --git a/file_manager.py b/file_manager.py new file mode 100644 index 0000000..9bdca6e --- /dev/null +++ b/file_manager.py @@ -0,0 +1,33 @@ +import argparse +from pathlib import Path + +import move_items +import undo + +def cli_move(args): + """整理:调用 move_items 的完整流程""" + move_items.main(Path(args.source)) + +def cli_undo(args): + """撤销:调用 undo 的完整流程""" + undo.main() + +def main(): + parser = argparse.ArgumentParser(description="文件管理器:按扩展名分类及对应撤销操作") + sub = parser.add_subparsers(dest="command", required=True) # 必须写子命令 + + # 移动操作的子命令解析器对象 + p_move = sub.add_parser("move", help="按扩展名整理文档") + p_move.add_argument("--source", "-s", required=True, help="要整理的文件夹路径") + p_move.set_defaults(func=cli_move) # 这行的作用是设置该解析器对象的函数(比写一堆 if 好的多) + + # 撤回操作的子命令解析器对象 + p_undo = sub.add_parser("undo", help="撤销某次操作") + p_undo.set_defaults(func=cli_undo) + + # 解析命令行参数 + args = parser.parse_args() + args.func(args) # 这行代码会执行 func 函数,并把参数 args 传进去 + +if __name__ == "__main__": + main() \ No newline at end of file From 17c04e0f1d37d1276f1e2196101b080c16223e64 Mon Sep 17 00:00:00 2001 From: Rick Anderson Date: Thu, 13 Aug 2026 23:59:33 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E5=9C=A8=20file=5Fmanager=20?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=AD=E5=8A=A0=E5=85=A5=E4=BA=86=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E8=BF=90=E8=A1=8C=E6=97=B6=E7=9A=84=E6=93=8D=E4=BD=9C?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=EF=BC=8C=E4=B8=8D=E5=86=8D=E5=AE=8C=E5=85=A8?= =?UTF-8?q?=E4=BE=9D=E8=B5=96=E5=91=BD=E4=BB=A4=E8=A1=8C=E3=80=82=20-=20?= =?UTF-8?q?=E7=8E=B0=E5=9C=A8=E4=B8=8D=E5=BC=BA=E5=88=B6=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E8=A1=8C=E8=BF=90=E8=A1=8C=20file=5Fmanager.?= =?UTF-8?q?py=20=E6=96=87=E4=BB=B6=E4=BA=86=20-=20=E6=AD=A4=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E7=9A=84=20file=5Fmanager.py=20=E5=8F=AF=E4=BB=A5?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5=E8=BD=AC=E5=8C=96=E4=B8=BA=E5=8F=AF=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E6=96=87=E4=BB=B6=E6=AD=A3=E5=B8=B8=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=20-=20=E6=96=B0=E5=A2=9E=20README.md=EF=BC=8C=E4=BB=8B?= =?UTF-8?q?=E7=BB=8D=E4=BA=86=E6=AD=A4=E7=89=88=E6=9C=AC=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E7=9A=84=E4=BD=BF=E7=94=A8=E6=96=B9=E6=B3=95=E3=80=82=EF=BC=88?= =?UTF-8?q?=E5=8F=AF=E4=BB=A5=E4=BD=BF=E7=94=A8=20-h=20=E6=88=96=20--help?= =?UTF-8?q?=20=E6=9F=A5=E7=9C=8B=E6=8C=87=E4=BB=A4=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 5 ++++- README.md | 37 +++++++++++++++++++++++++++++++++++ file_manager.py | 27 +++++++++++++++++++++++-- "\350\256\276\350\256\241.md" | 8 ++++++++ 4 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 README.md diff --git a/.gitignore b/.gitignore index 49c7c53..07710bd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,7 @@ log.jsonl config.json __pycache__/ -*.py[cod] \ No newline at end of file +*.py[cod] +build +dist +*.spec \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..0252940 --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# File_manager 文件整理器介绍 + +本命令行工具是刚学了一点 Python,一时兴起做的小东西,顺便用来了解一下做东西的大致流程。 + +此工具唯一的功能是把文件按照扩展名自动整理进不同文件夹,以及撤回任意批次的移动操作。它既可以独立运行(有提示页面),也支持命令行参数。 + +## 使用方法 + +### 方式一:在命令行工具中运行 Python 源文件 + +在源文件所在目录运行命令行,运行下列指令: + +- 整理操作:`python file_manager.py move -s "要整理的文件夹路径"` +- 撤销操作:`python file_manager.py undo` + +### 方式二:直接运行可执行文件或以无参数形式运行 Python 源文件 + +运行后,按照指示菜单进行操作即可。 + +## 说明 + +- 首次运行会生成配置文件 config.json(分类规则,可以自行配置)。 +- 配置文件损坏时,会使用程序内置的分类规则。这部分内容写在 categories.py 模块中。 +- 移动记录会写入日志 log.jsonl;撤销操作不会被记录。 +- 整理前会显示计划并询问确认。 + +## 反馈 + +创作者对所有想到的特殊情况都进行了处理,并在程序中 **以提示的形式写明**,但大致仍不免有疏漏。如果遇到了一些未处理的问题,欢迎到对应版本的 release 页面的 issues 或 comments 分区反馈。 + +此外,有任何建议,也都欢迎到 comments 中提出!我会尽可能完善! + + + +2026.8.13 + +Rick Anderson \ No newline at end of file diff --git a/file_manager.py b/file_manager.py index 9bdca6e..aadebee 100644 --- a/file_manager.py +++ b/file_manager.py @@ -12,9 +12,29 @@ def cli_undo(args): """撤销:调用 undo 的完整流程""" undo.main() +def interactive_menu() -> None: + """无参数双击时的交互菜单""" + while True: + print("\n=== 文件整理工具 ===") + print("1. 整理文件(按扩展名分类)") + print("2. 撤销某次整理") + print("q. 退出") + choice = input("请选择: ").strip().lower() + + if choice == "1": + src = input("请输入要整理的文件夹路径: ").strip().strip('"').strip("'") + move_items.main(Path(src)) + elif choice == "2": + undo.main() + elif choice in ("q", "quit"): + break + else: + print("无效输入,请重新输入") + + def main(): parser = argparse.ArgumentParser(description="文件管理器:按扩展名分类及对应撤销操作") - sub = parser.add_subparsers(dest="command", required=True) # 必须写子命令 + sub = parser.add_subparsers(dest="command") # 无参数时 args.command 为 None,进入交互菜单 # 移动操作的子命令解析器对象 p_move = sub.add_parser("move", help="按扩展名整理文档") @@ -27,7 +47,10 @@ def main(): # 解析命令行参数 args = parser.parse_args() - args.func(args) # 这行代码会执行 func 函数,并把参数 args 传进去 + if args.command is None: + interactive_menu() # 无子命令(如双击 exe)→ 交互菜单 + else: + args.func(args) # 有子命令 → 执行对应的处理函数 if __name__ == "__main__": main() \ No newline at end of file diff --git "a/\350\256\276\350\256\241.md" "b/\350\256\276\350\256\241.md" index 469f2db..37d73f5 100644 --- "a/\350\256\276\350\256\241.md" +++ "b/\350\256\276\350\256\241.md" @@ -189,3 +189,11 @@ def write_log(item: Path, target: Path, ts: str): 分类依据则不相同,目前并不违背不重复原则,但是完善的默认分类依据实在是太长了(80 行),不太适合直接放进 move_items.py 文件中,因此这里也将其拆到了单独的 `categories.py` 文件中。 以上二者都通过 `from ... import ...` 语句访问。 + +### 3. 合并与打包 + +尽管可以做更多功能,但是当前是相当简洁的一个版本,不妨在这里就开始打包练手。 + +首先,要写一个模块 `file_manager.py` 用来整合两个操作模块,并提供外界操作方式,换句话说,也就是路由。提供 CLI 接口的方法可见 [`argparse`](https://docs.python.org/zh-cn/3/library/argparse.html#module-argparse) 教程(当然,依然建议看别的教程,官方教程写的让人头晕);而以 exe 形式运行的时候是没法写参数的,所以额外提供了一个命令菜单,用来指示操作。 + +合并用的主模块写完以后,要进行几遍测试。测试完毕之后,就可以打包成 .exe 文件了。该文件存在 dist 文件夹中。 From 5f6a79abf25ce36f7b0088493334a69b50db90e1 Mon Sep 17 00:00:00 2001 From: Rick Anderson Date: Fri, 14 Aug 2026 23:47:42 +0800 Subject: [PATCH 4/4] =?UTF-8?q?docs:=20=E9=A1=B9=E7=9B=AE=E6=80=BB?= =?UTF-8?q?=E7=BB=93=E3=80=82=E9=A1=BA=E4=BE=BF=E6=B5=8B=E8=AF=95=20git=20?= =?UTF-8?q?=E9=87=8D=E6=96=B0=E4=B8=8B=E8=BD=BD=E4=BC=9A=E6=9C=89=E4=BB=80?= =?UTF-8?q?=E4=B9=88=E5=BD=B1=E5=93=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- "\350\256\276\350\256\241.md" | 6 ++- ...71\347\233\256\346\200\273\347\273\223.md" | 43 +++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 "\351\241\271\347\233\256\346\200\273\347\273\223.md" diff --git a/README.md b/README.md index 0252940..f1248ec 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ ## 反馈 -创作者对所有想到的特殊情况都进行了处理,并在程序中 **以提示的形式写明**,但大致仍不免有疏漏。如果遇到了一些未处理的问题,欢迎到对应版本的 release 页面的 issues 或 comments 分区反馈。 +创作者对所有想到的特殊情况都进行了处理,并在程序中 **以提示的形式写明**,但大致仍不免有疏漏。如果遇到了一些未处理的问题,欢迎到 issues 或 comments 分区反馈。 此外,有任何建议,也都欢迎到 comments 中提出!我会尽可能完善! diff --git "a/\350\256\276\350\256\241.md" "b/\350\256\276\350\256\241.md" index 37d73f5..ba2342d 100644 --- "a/\350\256\276\350\256\241.md" +++ "b/\350\256\276\350\256\241.md" @@ -1,6 +1,6 @@ # 文件管理 - 设计 -## v1 +## v1.0.0 ### 1. 核心功能 @@ -197,3 +197,7 @@ def write_log(item: Path, target: Path, ts: str): 首先,要写一个模块 `file_manager.py` 用来整合两个操作模块,并提供外界操作方式,换句话说,也就是路由。提供 CLI 接口的方法可见 [`argparse`](https://docs.python.org/zh-cn/3/library/argparse.html#module-argparse) 教程(当然,依然建议看别的教程,官方教程写的让人头晕);而以 exe 形式运行的时候是没法写参数的,所以额外提供了一个命令菜单,用来指示操作。 合并用的主模块写完以后,要进行几遍测试。测试完毕之后,就可以打包成 .exe 文件了。该文件存在 dist 文件夹中。 + + + +## diff --git "a/\351\241\271\347\233\256\346\200\273\347\273\223.md" "b/\351\241\271\347\233\256\346\200\273\347\273\223.md" new file mode 100644 index 0000000..32f3696 --- /dev/null +++ "b/\351\241\271\347\233\256\346\200\273\347\273\223.md" @@ -0,0 +1,43 @@ +# 项目总结 + +## 一、v1.0.0 的创作 + +### 1. 创作动机 + +虽然我不是学计算机的,而且科大的培养计划里面,大一的课程也不重专业课,只有作为全校计算机通修课程的《计算机程序设计》(实际上约等于 C 语言基础)和信息学院的专业基础课《数据结构及其算法》与此相关。本人这两门课学得也很垃圾,虽然数据结构混到了优秀,但是作业基本上是照课本抄的,实验基本是 AI 做的。假期顺便学了一点 Python,打算做一点真正能用的东西来做训练。再三思考之下决定模仿一些系统自带的分类功能,做一个自动分类小工具。不得不说 Python 的标准库是真的强大……同时也让我不免怀疑,以 C++ 的方式加上它庞大的标准库,学起来到底要多困难…… + +### 2. 设计流程 + +在做数据结构课程的实验时,我对如何做一个东西并没有特别清楚的认识,甚至一开始分不清楚一个项目的业务逻辑和数据结构本身的完成,导致设计得极为抽象且低效。而这次做这个工具,我的打算就是训练这方面的能力:拆分逻辑、逐项完成和测试、版本管理、处理问题。 + +大概的逻辑可以如下划分: + +``` +设计主要功能: + 大致规划这些功能所需的数据结构(本例中,只需要线性结构,所以日志结构非常简单) + 分别完成每一个功能: + 将一个功能拆分为多个简单逻辑(可以直接写出代码的) + 实现每个逻辑 + 独立测试 + 将多个功能串联起来,测试整体功能是否满足 + 修复和补充功能,重复上述部分步骤 + 串联起来,测试整体功能 + 修复、补充 +``` + +- 一开始设计的只有分类功能,完成以后为其添加了预显示即将移动的文件并提供确认或取消的功能; + +- 随后,考虑到移动多个文件的话,如果需要手动恢复不太容易,所以想要添加撤回功能,而这就需要日志; + +- 一开始的日志是简单的“位置参数”形式,即 `时间 被移动文件的路径 移动后的文件路径`,后来考虑到可能后面要加上更多功能,日志里面可能要记录更多形式的东西,所以将此种日志改为了 json line 格式,每一行都注明了操作类型(虽然现在只有 "move" 一种); + +- 随后设计了能一次性撤回任意批次或全部操作的撤回功能; +- 此时,主要功能已经完工,要进行一些使用上的优化和问题的修复。 + - 首先是提供了初级的操控,使得 move_items.py 模块在作为脚本单独运行时可以由用户输入待整理的文件夹目录(此前是写死在代码中的 "D:/test/") + - 随后,为做成可执行文件进行准备,将原先写在 move_items.py 模块中的分类依据变成了“默认值”,如果存在配置文件,就不会调用 move_items.py 中的分类依据;如果不存在配置文件或其已损坏,就使用默认值。 + - 之后,由于寻找程序所在目录在多个模块中都要用到,所以就单独放进了一个模块以供调用;而完善的分类依据实在是太长了,放在 move_items.py 模块中有点妨碍查看其真正重要的逻辑和功能,所以也放进了一个单独的模块中。 + - 至此,基本没有我已经想到且想要加入的功能了。于是开始着手提供一个操控方式,也即 file_manager.py。一开始,只做了支持命令行参数的版本,但是发现可执行文件不支持这样的操作,所以又加入了直接以无参数方式运行时的操作界面。这个版本的可执行文件通过了测试,于是就直接发布了。 + +### 3. 收获与总结 + +这次亲手制作小工具的确很有收获。首先是做这种小项目的流程上,比之前要规范、高效了很多;其次在制作过程中,一边思考思路一边去学习可能用到的工具,对此也有了更多领悟。当然,具体是什么,我或许自己知道,但说不太清楚。 \ No newline at end of file