diff --git a/.gitignore b/.gitignore index b321710..7f08f94 100644 --- a/.gitignore +++ b/.gitignore @@ -198,6 +198,8 @@ temp/ *.orig # Project specific +dist_package/ +*.zip *.pyc *.pyo *.pyd diff --git a/CHANGELOG.md b/CHANGELOG.md index 823f7b9..581bc00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,8 +55,36 @@ - 社区功能和内容分享 -## [0.2.0] - 2025-10-16 +## [0.2.0] - 2025-10-19 + +### 新增 +- 实现完整的天气功能集成 +- 添加自动IP定位功能,自动获取用户地理位置 +- 支持中英文城市名智能映射(如:Tianjin → 天津) +- 扩展城市支持到40+个主要城市 +- 添加4个不同的IP定位API接口(搜狐、pconline、ip-api、淘宝) +- 实现天气数据缓存和状态栏显示 +- 添加城市选择下拉菜单功能 +- 集成3天天气预报功能 +- 添加详细的错误处理和调试日志 + +### 更改 +- 重构天气API集成架构 +- 优化城市ID映射机制 +- 改进错误处理和用户反馈 +- 增强网络请求稳定性 +- 优化UI界面布局和响应速度 ### 修复 -- 页面更新到Microsoft Word -- 现在可以在软件中查看部分城市天气,或者显示IP属地天气. \ No newline at end of file +- 修复KeyError天气数据访问问题 +- 修复自动定位功能失败问题 +- 修复城市ID映射错误 +- 修复网络请求超时和异常处理 +- 修复界面状态更新问题 +- 修复中英文城市名混用问题 + +### 技术改进 +- 实现多重IP定位备份机制 +- 添加智能城市名解析和映射 +- 优化API调用性能和错误恢复 +- 增强代码模块化和可维护性 \ No newline at end of file diff --git a/build_release.py b/build_release.py new file mode 100644 index 0000000..9ad0294 --- /dev/null +++ b/build_release.py @@ -0,0 +1,255 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +MagicWord 0.2.0 版本发布脚本 +用于构建和打包应用程序 +""" + +import os +import sys +import subprocess +import platform +import shutil +import zipfile +from datetime import datetime + +def run_command(command, shell=False, cwd=None): + """运行命令并返回结果""" + try: + result = subprocess.run(command, shell=shell, capture_output=True, text=True, encoding='utf-8', cwd=cwd) + return result.returncode, result.stdout, result.stderr + except Exception as e: + return -1, "", str(e) + +def clean_build_dirs(): + """清理构建目录""" + print("清理构建目录...") + dirs_to_clean = ['build', 'dist', '__pycache__', '*.egg-info'] + + for dir_name in dirs_to_clean: + if '*' in dir_name: + # 处理通配符 + import glob + for path in glob.glob(dir_name): + if os.path.isdir(path): + shutil.rmtree(path, ignore_errors=True) + elif os.path.exists(dir_name): + if os.path.isdir(dir_name): + shutil.rmtree(dir_name, ignore_errors=True) + else: + os.remove(dir_name) + + # 清理src目录下的__pycache__ + for root, dirs, files in os.walk('src'): + for dir_name in dirs: + if dir_name == '__pycache__': + cache_path = os.path.join(root, dir_name) + shutil.rmtree(cache_path, ignore_errors=True) + print(f"清理缓存: {cache_path}") + +def install_dependencies(): + """安装依赖""" + print("安装项目依赖...") + code, stdout, stderr = run_command([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"]) + if code != 0: + print(f"依赖安装失败: {stderr}") + return False + print("依赖安装成功") + return True + +def build_executable(): + """构建可执行文件""" + print("构建可执行文件...") + + # 安装pyinstaller + print("安装PyInstaller...") + code, stdout, stderr = run_command([sys.executable, "-m", "pip", "install", "pyinstaller"]) + if code != 0: + print(f"PyInstaller安装失败: {stderr}") + return False + + # PyInstaller命令 + pyinstaller_cmd = [ + "pyinstaller", + "--name", "MagicWord", + "--version", "0.2.0", + "--distpath", "dist", + "--workpath", "build", + "--specpath", ".", + "--add-data", "resources;resources", + "--add-data", "src;src", + "--hidden-import", "PyQt5", + "--hidden-import", "PyQt5.QtCore", + "--hidden-import", "PyQt5.QtGui", + "--hidden-import", "PyQt5.QtWidgets", + "--hidden-import", "requests", + "--hidden-import", "beautifulsoup4", + "--hidden-import", "python-docx", + "--hidden-import", "PyPDF2", + "--hidden-import", "ebooklib", + "--hidden-import", "chardet", + "--hidden-import", "PIL", + "--icon", "resources/icons/app_icon.ico", + "--windowed", # 无控制台窗口 + "--noconfirm", + "src/main.py" + ] + + # Windows平台特殊处理 + if platform.system() == "Windows": + pyinstaller_cmd.extend(["--add-binary", "resources;resources"]) + + print("运行PyInstaller...") + code, stdout, stderr = run_command(pyinstaller_cmd) + + if code != 0: + print(f"构建失败: {stderr}") + print("尝试简化构建...") + # 简化版本 + simple_cmd = [ + "pyinstaller", + "--onefile", + "--windowed", + "--icon=resources/icons/app_icon.ico", + "--add-data=resources;resources", + "--name=MagicWord", + "src/main.py" + ] + code, stdout, stderr = run_command(simple_cmd) + if code != 0: + print(f"简化构建也失败: {stderr}") + return False + + print("可执行文件构建成功") + return True + +def create_package(): + """创建发布包""" + print("创建发布包...") + + # 检查构建结果 + if platform.system() == "Windows": + exe_path = "dist/MagicWord.exe" + else: + exe_path = "dist/MagicWord" + + if not os.path.exists(exe_path): + print(f"错误: 找不到可执行文件 {exe_path}") + return False + + # 创建发布目录 + release_dir = "dist_package" + if os.path.exists(release_dir): + shutil.rmtree(release_dir) + os.makedirs(release_dir) + + # 复制文件到发布目录 + files_to_copy = [ + (exe_path, "MagicWord.exe" if platform.system() == "Windows" else "MagicWord"), + ("README.md", "README.md"), + ("CHANGELOG.md", "CHANGELOG.md"), + ("requirements.txt", "requirements.txt"), + ("install_and_fix.py", "install_and_fix.py"), + ] + + for src, dst in files_to_copy: + if os.path.exists(src): + shutil.copy2(src, os.path.join(release_dir, dst)) + print(f"复制: {src} -> {dst}") + + # 创建运行脚本 + if platform.system() == "Windows": + run_script = """@echo off +echo MagicWord 0.2.0 启动中... +cd /d "%~dp0" +start MagicWord.exe +""" + with open(os.path.join(release_dir, "run.bat"), "w") as f: + f.write(run_script) + else: + run_script = """#!/bin/bash +echo "MagicWord 0.2.0 启动中..." +cd "$(dirname "$0")" +./MagicWord & +""" + with open(os.path.join(release_dir, "run.sh"), "w") as f: + f.write(run_script) + os.chmod(os.path.join(release_dir, "run.sh"), 0o755) + + # 创建发布说明 + release_info = f"""MagicWord 0.2.0 发布包 +构建时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} +平台: {platform.system()} {platform.machine()} +Python版本: {platform.python_version()} + +快速开始: +1. 运行 install_and_fix.py 安装依赖 +2. 运行 run.bat (Windows) 或 run.sh (Linux/Mac) +3. 或直接运行 MagicWord.exe + +主要更新: +- 完整的天气功能集成 +- 自动IP定位功能 +- 40+个城市支持 +- 中英文城市名智能映射 + +详细更新请查看 CHANGELOG.md +""" + + with open(os.path.join(release_dir, "RELEASE_INFO.txt"), "w", encoding="utf-8") as f: + f.write(release_info) + + # 创建ZIP包 + zip_name = f"MagicWord_v0.2.0_{platform.system()}_{platform.machine()}.zip" + with zipfile.ZipFile(zip_name, 'w', zipfile.ZIP_DEFLATED) as zipf: + for root, dirs, files in os.walk(release_dir): + for file in files: + file_path = os.path.join(root, file) + arc_name = os.path.relpath(file_path, release_dir) + zipf.write(file_path, arc_name) + + print(f"发布包创建成功: {zip_name}") + return True + +def main(): + """主函数""" + print("=" * 60) + print("MagicWord 0.2.0 版本发布构建脚本") + print("=" * 60) + + # 检查Python版本 + print(f"Python版本: {platform.python_version()}") + print(f"操作系统: {platform.system()} {platform.machine()}") + + # 清理构建目录 + clean_build_dirs() + + # 安装依赖 + print("\n安装依赖...") + if not install_dependencies(): + print("依赖安装失败") + sys.exit(1) + + # 构建可执行文件 + print("\n构建可执行文件...") + if not build_executable(): + print("构建失败,尝试手动构建...") + # 如果自动构建失败,提供手动构建说明 + print("\n手动构建步骤:") + print("1. 安装PyInstaller: pip install pyinstaller") + print("2. 运行: pyinstaller --onefile --windowed --icon=resources/icons/app_icon.ico src/main.py") + sys.exit(1) + + # 创建发布包 + print("\n创建发布包...") + if not create_package(): + print("发布包创建失败") + sys.exit(1) + + print("\n" + "=" * 60) + print("发布构建完成!") + print("发布包已创建在当前目录下") + print("=" * 60) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/create_manual_package.py b/create_manual_package.py new file mode 100644 index 0000000..85f297c --- /dev/null +++ b/create_manual_package.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +MagicWord 0.2.0 手动发布包创建脚本 +""" + +import os +import shutil +import zipfile +from datetime import datetime + +def create_manual_package(): + """创建手动发布包""" + print("创建 MagicWord 0.2.0 手动发布包...") + + # 创建发布目录 + release_dir = "dist_package" + if os.path.exists(release_dir): + shutil.rmtree(release_dir) + os.makedirs(release_dir) + + # 复制主要文件 + files_to_copy = [ + ("README.md", "README.md"), + ("CHANGELOG.md", "CHANGELOG.md"), + ("requirements.txt", "requirements.txt"), + ("setup.py", "setup.py"), + ("install_and_fix.py", "install_and_fix.py"), + ] + + for src, dst in files_to_copy: + if os.path.exists(src): + shutil.copy2(src, os.path.join(release_dir, dst)) + print(f"复制: {src} -> {dst}") + else: + print(f"警告: 文件 {src} 不存在") + + # 复制源代码 + src_dir = os.path.join(release_dir, "src") + if os.path.exists("src"): + shutil.copytree("src", src_dir) + print("复制源代码目录") + else: + print("警告: src 目录不存在") + + # 复制资源文件 + resources_dir = os.path.join(release_dir, "resources") + if os.path.exists("resources"): + shutil.copytree("resources", resources_dir) + print("复制资源文件目录") + else: + print("警告: resources 目录不存在") + + # 创建运行脚本 + run_bat = """@echo off +echo MagicWord 0.2.0 启动中... +cd /d "%~dp0" +python src/main.py +pause +""" + + run_sh = """#!/bin/bash +echo "MagicWord 0.2.0 启动中..." +cd "$(dirname "$0")" +python3 src/main.py +""" + + with open(os.path.join(release_dir, "run.bat"), "w", encoding="utf-8") as f: + f.write(run_bat) + + with open(os.path.join(release_dir, "run.sh"), "w", encoding="utf-8") as f: + f.write(run_sh) + + # 创建安装脚本 + install_bat = """@echo off +echo 正在安装 MagicWord 0.2.0 依赖... +python -m pip install -r requirements.txt +echo 安装完成! +pause +""" + + with open(os.path.join(release_dir, "install.bat"), "w", encoding="utf-8") as f: + f.write(install_bat) + + # 创建发布说明 + release_info = f"""MagicWord 0.2.0 手动发布包 +构建时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} + +安装和运行说明: + +1. 安装依赖: + - Windows: 运行 install.bat + - Linux/Mac: pip install -r requirements.txt + +2. 运行应用: + - Windows: 运行 run.bat + - Linux/Mac: 运行 run.sh 或 python3 src/main.py + +3. 主要功能: + - 完整的天气功能集成 + - 自动IP定位功能 + - 40+个城市支持 + - 中英文城市名智能映射 + - Microsoft Word风格界面 + +详细更新请查看 CHANGELOG.md + +注意: 需要Python 3.6+环境 +""" + + with open(os.path.join(release_dir, "RELEASE_INFO.txt"), "w", encoding="utf-8") as f: + f.write(release_info) + + # 创建ZIP包 + zip_name = f"MagicWord_v0.2.0_Manual_Package.zip" + with zipfile.ZipFile(zip_name, 'w', zipfile.ZIP_DEFLATED) as zipf: + for root, dirs, files in os.walk(release_dir): + for file in files: + file_path = os.path.join(root, file) + arc_name = os.path.relpath(file_path, release_dir) + zipf.write(file_path, arc_name) + + print(f"\n发布包创建成功: {zip_name}") + print(f"包含文件数量: {sum(len(files) for _, _, files in os.walk(release_dir))}") + + return True + +def verify_package(): + """验证发布包内容""" + print("\n验证发布包内容...") + + release_dir = "dist_package" + if not os.path.exists(release_dir): + print("发布目录不存在") + return False + + # 检查关键文件 + required_files = [ + "README.md", + "CHANGELOG.md", + "requirements.txt", + "src/main.py", + "src/ui/word_style_ui.py", + "run.bat", + "run.sh", + "install.bat", + "RELEASE_INFO.txt" + ] + + missing_files = [] + for file_path in required_files: + full_path = os.path.join(release_dir, file_path) + if os.path.exists(full_path): + print(f"✓ {file_path}") + else: + print(f"✗ {file_path}") + missing_files.append(file_path) + + if missing_files: + print(f"\n缺失文件: {', '.join(missing_files)}") + return False + + print("\n发布包验证通过!") + return True + +def main(): + """主函数""" + print("=" * 60) + print("MagicWord 0.2.0 手动发布包创建工具") + print("=" * 60) + + # 创建发布包 + if not create_manual_package(): + print("发布包创建失败") + return 1 + + # 验证发布包 + if not verify_package(): + print("发布包验证失败") + return 1 + + print("\n" + "=" * 60) + print("手动发布包创建完成!") + print("用户需要手动安装Python依赖并运行应用") + print("=" * 60) + + return 0 + +if __name__ == "__main__": + import sys + sys.exit(main()) \ No newline at end of file diff --git a/dist_package/MagicWord_v0.1.0_Windows_x86.zip b/dist_package/MagicWord_v0.1.0_Windows_x86.zip deleted file mode 100644 index 2b535fc..0000000 Binary files a/dist_package/MagicWord_v0.1.0_Windows_x86.zip and /dev/null differ diff --git a/setup.py b/setup.py index bfabcde..d04cb7b 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="MagicWord", - version="0.1.0", + version="0.2.0", description="隐私学习软件 - 一款通过打字练习来学习文档内容的工具", author="MagicWord Team", packages=find_packages(where="src"), diff --git a/test_build.py b/test_build.py new file mode 100644 index 0000000..631d9fc --- /dev/null +++ b/test_build.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +MagicWord 0.2.0 版本测试构建脚本 +简化版本用于测试功能完整性 +""" + +import os +import sys +import subprocess +from datetime import datetime + +def test_imports(): + """测试所有依赖是否可以正常导入""" + print("测试依赖导入...") + + required_modules = [ + 'PyQt5', + 'requests', + 'bs4', + 'docx', + 'PyPDF2', + 'ebooklib', + 'PIL', + 'chardet' + ] + + failed_modules = [] + + for module in required_modules: + try: + if module == 'bs4': + import bs4 + elif module == 'docx': + import docx + elif module == 'PIL': + import PIL + else: + __import__(module) + print(f"✓ {module}") + except ImportError as e: + print(f"✗ {module}: {e}") + failed_modules.append(module) + + if failed_modules: + print(f"\n导入失败的模块: {', '.join(failed_modules)}") + return False + + print("所有依赖导入成功!") + return True + +def test_source_files(): + """测试源代码文件是否存在""" + print("\n检查源代码文件...") + + required_files = [ + 'src/main.py', + 'src/main_window.py', + 'src/ui/word_style_ui.py', + 'src/file_manager/file_operations.py', + 'src/input_handler/input_processor.py', + 'src/services/network_service.py', + 'resources/icons/app_icon.ico', + 'resources/config/app_settings.json' + ] + + missing_files = [] + + for file_path in required_files: + if os.path.exists(file_path): + print(f"✓ {file_path}") + else: + print(f"✗ {file_path}") + missing_files.append(file_path) + + if missing_files: + print(f"\n缺失的文件: {', '.join(missing_files)}") + return False + + print("所有源代码文件检查通过!") + return True + +def test_version_info(): + """测试版本信息""" + print("\n检查版本信息...") + + # 检查setup.py + if os.path.exists('setup.py'): + with open('setup.py', 'r', encoding='utf-8') as f: + content = f.read() + if 'version="0.2.0"' in content: + print("✓ setup.py 版本号正确") + else: + print("✗ setup.py 版本号不正确") + return False + + # 检查CHANGELOG.md + if os.path.exists('CHANGELOG.md'): + with open('CHANGELOG.md', 'r', encoding='utf-8') as f: + content = f.read() + if '0.2.0' in content: + print("✓ CHANGELOG.md 包含0.2.0版本信息") + else: + print("✗ CHANGELOG.md 缺少0.2.0版本信息") + return False + + print("版本信息检查通过!") + return True + +def test_city_mapping(): + """测试城市映射功能""" + print("\n测试城市映射功能...") + + try: + # 尝试导入城市映射 + sys.path.append('src') + from ui.word_style_ui import city_id_map + + # 检查一些主要城市 + test_cities = [ + ('Beijing', '北京'), + ('Shanghai', '上海'), + ('Guangzhou', '广州'), + ('Shenzhen', '深圳'), + ('Chengdu', '成都'), + ('Hangzhou', '杭州') + ] + + for eng_name, chn_name in test_cities: + if eng_name in city_id_map: + mapped_name = city_id_map[eng_name] + if mapped_name == chn_name: + print(f"✓ {eng_name} -> {mapped_name}") + else: + print(f"⚠ {eng_name} -> {mapped_name} (期望: {chn_name})") + else: + print(f"✗ {eng_name} 未找到映射") + + print(f"城市映射表包含 {len(city_id_map)} 个城市") + return True + + except Exception as e: + print(f"城市映射测试失败: {e}") + return False + +def create_test_report(): + """创建测试报告""" + print("\n" + "="*60) + print("MagicWord 0.2.0 版本功能测试报告") + print("="*60) + print(f"测试时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}") + + tests = [ + ("依赖导入测试", test_imports), + ("源代码文件检查", test_source_files), + ("版本信息检查", test_version_info), + ("城市映射功能测试", test_city_mapping) + ] + + passed = 0 + total = len(tests) + + for test_name, test_func in tests: + print(f"\n[{test_name}]") + if test_func(): + passed += 1 + print(f"✓ {test_name} 通过") + else: + print(f"✗ {test_name} 失败") + + print(f"\n测试结果: {passed}/{total} 通过") + + if passed == total: + print("\n✓ 所有测试通过! 版本准备就绪") + return True + else: + print(f"\n✗ {total - passed} 个测试失败, 需要修复") + return False + +def main(): + """主函数""" + success = create_test_report() + + if success: + print("\n建议下一步:") + print("1. 运行应用进行手动测试") + print("2. 创建发布包") + else: + print("\n请先修复测试中发现的问题") + + return 0 if success else 1 + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file