diff --git a/HarmonyDevTools_Python/.gitignore b/HarmonyDevTools_Python/.gitignore new file mode 100644 index 0000000..3f641bf --- /dev/null +++ b/HarmonyDevTools_Python/.gitignore @@ -0,0 +1,106 @@ +# Python运行时文件 +__pycache__/ +*.py[cod] +*$py.class +*.so + +# 分发/打包 +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +*.manifest +*.spec + +# 单元测试/覆盖率报告 +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# 翻译 +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# 项目特定文件 +config.json +harmony_dev_tools.log +*.exe +*.spec + +# 导出目录(时间戳格式) +[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]-[0-9][0-9]/ diff --git a/HarmonyDevTools_Python/README_Python.md b/HarmonyDevTools_Python/README_Python.md new file mode 100644 index 0000000..b4a295a --- /dev/null +++ b/HarmonyDevTools_Python/README_Python.md @@ -0,0 +1,183 @@ +# HarmonyDevTools - Python版本 + +![](https://img.shields.io/badge/状态-稳定-red.svg) +![](https://img.shields.io/badge/启动时间-2024/06/20-green.svg) +![](https://img.shields.io/badge/优先级-NORMAL-blue.svg) +![](https://img.shields.io/badge/语言-Python-blue.svg) + +本项目为HarmonyOS/OpenHarmony的 toolchains 工具提供Python Tkinter UI操作界面,方便使用。 + +## 1. 版本说明 + +这是原C# WPF项目的Python Tkinter版本,保持了相同的功能特性: + +hdc功能支持: +- 枚举设备/连接设备 +- 重启hdc +- 版本信息显示 +- 安装、卸载应用 +- 导出照片 +- 获取手机的UDID +- 其他命令的执行(手动输入执行) + +## 2. 环境要求 + +- Python 3.7+ +- Windows操作系统 +- HarmonyOS/OpenHarmony SDK(包含toolchains目录) + +## 3. 安装和运行 + +### 3.1 直接运行 + +1. 确保已安装Python 3.7或更高版本 +2. 下载项目文件 +3. 确保`toolchains`目录包含`hdc.exe`文件 +4. 运行主程序: + +**方式一:使用启动脚本(推荐)** +```bash +run.bat +``` + +**方式二:直接运行Python文件** +```bash +python main.py +``` + +### 3.2 安装依赖(可选) + +如果需要额外的功能,可以安装可选依赖: + +```bash +pip install -r requirements.txt +``` + +## 4. 打包为exe文件 + +### 4.1 使用项目自带的构建脚本(推荐) + +```bash +# 运行构建脚本 +python build.py +``` + +构建脚本会: +- 自动检查PyInstaller是否安装 +- 自动清理之前的构建文件 +- 构建HarmonyDevTools.exe +- 自动创建完整的发布包 +- 生成启动脚本和说明文档 + +### 4.2 使用PyInstaller手动打包 + +```bash +# 安装PyInstaller +pip install pyinstaller + +# 打包为exe文件 +pyinstaller --onefile --windowed --name HarmonyDevTools main.py +``` + +### 4.3 使用auto-py-to-exe(GUI工具) + +```bash +# 安装auto-py-to-exe +pip install auto-py-to-exe + +# 启动GUI打包工具 +auto-py-to-exe +``` + +## 5. 项目结构 + +``` +HarmonyDevTools_Python/ +├── main.py # 主程序文件 +├── requirements.txt # Python依赖文件 +├── README_Python.md # 说明文档 +├── toolchains/ # 工具链目录 +│ ├── hdc.exe # HDC工具 +│ └── libusb_shared.dll +└── build/ # 打包输出目录(可选) +``` + +## 6. 功能说明 + +### 6.1 设备管理 +- **列举设备**: 显示所有可用的HarmonyOS设备 +- **连接设备**: 通过connect key连接指定设备 +- **重启hdc**: 重启HDC服务 + +### 6.2 应用管理 +- **安装hap**: 安装HarmonyOS应用包 + - 替换安装: 覆盖已存在的应用 + - 允许降级: 允许安装较低版本 + - 动态授权: 动态授权安装 +- **卸载应用**: 根据包名卸载应用 + +### 6.3 文件操作 +- **导出照片**: 从设备导出照片到本地 +- **自定义命令**: 执行任意hdc命令 + +### 6.4 系统信息 +- **版本信息**: 显示HDC版本信息 +- **UDID**: 获取设备唯一标识符 + +## 7. 与原C#版本的对比 + +| 特性 | C# WPF版本 | Python Tkinter版本 | +|------|------------|-------------------| +| 运行环境 | .NET Core 6.0 | Python 3.7+ | +| UI框架 | WPF | Tkinter | +| 打包大小 | 较大 | 较小 | +| 跨平台 | Windows | Windows/Linux/macOS | +| 开发难度 | 中等 | 简单 | +| 维护成本 | 中等 | 低 | + +## 8. 故障排除 + +### 8.1 常见问题 + +1. **找不到hdc.exe** + - 确保`toolchains`目录存在且包含`hdc.exe` + - 检查文件路径是否正确 + +2. **Python未安装** + - 从[Python官网](https://www.python.org/downloads/)下载并安装Python + +3. **tkinter模块错误** + - 大多数Python安装都包含tkinter + - 如果缺失,请重新安装Python并确保选中tkinter选项 + +### 8.2 日志查看 + +程序运行时会输出日志信息,可以通过查看控制台输出来诊断问题。 + +## 9. 开发说明 + +### 9.1 代码结构 + +- `HdcUtil`: HDC工具类,负责执行命令 +- `HarmonyDevTools`: 主界面类,负责UI交互 +- `main()`: 程序入口函数 + +### 9.2 扩展功能 + +如需添加新功能,可以: + +1. 在`HdcUtil`类中添加新的静态方法 +2. 在`HarmonyDevTools`类中添加对应的UI元素和事件处理 +3. 更新UI布局 + +## 10. 许可证 + +本项目遵循原项目的许可证条款。 + +## 11. 贡献 + +欢迎提交Issue和Pull Request来改进这个项目。 + +--- + +**注意**: 使用前请确保已正确安装HarmonyOS/OpenHarmony SDK,并且`toolchains`目录包含所需的工具文件。 diff --git a/HarmonyDevTools_Python/build.py b/HarmonyDevTools_Python/build.py new file mode 100644 index 0000000..4aaea42 --- /dev/null +++ b/HarmonyDevTools_Python/build.py @@ -0,0 +1,213 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +HarmonyDevTools Python版本打包脚本 +用于将Python项目打包为exe文件 +""" + +import os +import sys +import subprocess +import shutil +from pathlib import Path + +def check_pyinstaller(): + """检查PyInstaller是否已安装""" + try: + import PyInstaller + return True + except ImportError: + return False + +def install_pyinstaller(): + """安装PyInstaller""" + print("正在安装PyInstaller...") + try: + subprocess.check_call([sys.executable, "-m", "pip", "install", "pyinstaller"]) + print("PyInstaller安装成功!") + return True + except subprocess.CalledProcessError: + print("PyInstaller安装失败!") + return False + +def build_exe(): + """构建exe文件""" + print("开始构建exe文件...") + + # 检查主程序文件是否存在 + if not os.path.exists("main.py"): + print("错误:找不到main.py文件!") + return False + + main_file = "main.py" + exe_name = "HarmonyDevTools" + print("构建HarmonyDevTools...") + + # 检查toolchains目录是否存在 + if not os.path.exists("toolchains"): + print("警告:找不到toolchains目录,请确保包含hdc.exe文件") + + # 构建命令 + cmd = [ + "pyinstaller", + "--onefile", # 打包为单个文件 + "--windowed", # 无控制台窗口 + f"--name={exe_name}", # 输出文件名 + "--icon=icon.ico", # 图标文件(如果存在) + "--add-data=toolchains;toolchains", # 包含toolchains目录 + main_file + ] + + # 如果图标文件不存在,移除图标参数 + if not os.path.exists("icon.ico"): + cmd = [arg for arg in cmd if not arg.startswith("--icon")] + + try: + subprocess.check_call(cmd) + print("构建成功!") + return exe_name + except subprocess.CalledProcessError as e: + print(f"构建失败:{e}") + return False + +def create_distribution(exe_name="HarmonyDevTools"): + """创建发布包""" + print("创建发布包...") + + # 直接使用dist目录作为发布目录 + dist_dir = "dist" + if not os.path.exists(dist_dir): + os.makedirs(dist_dir) + + # 复制toolchains目录到dist + if os.path.exists("toolchains"): + toolchains_dest = os.path.join(dist_dir, "toolchains") + if os.path.exists(toolchains_dest): + shutil.rmtree(toolchains_dest) + shutil.copytree("toolchains", toolchains_dest) + print("已复制toolchains目录到dist") + + # 创建适合exe发布包的README + create_release_readme(dist_dir) + + print(f"发布包已创建:{dist_dir}") + print("发布包包含:") + print(f"- {exe_name}.exe") + print("- toolchains/目录") + print("- README.md") + return True + +def create_release_readme(dist_dir): + """创建发布版README文件""" + readme_content = """# HarmonyDevTools + +HarmonyOS/OpenHarmony开发工具,提供图形化界面操作HDC工具。 + +## 功能特性 + +- **设备管理**: 列举设备、连接设备、重启HDC服务 +- **应用管理**: 安装/卸载HarmonyOS应用包 +- **文件操作**: 导出设备照片、导出应用日志 +- **系统信息**: 获取设备UDID、查看版本信息 +- **命令执行**: 支持执行任意HDC命令 + +## 使用方法 + +1. **启动程序**: 双击 `HarmonyDevTools.exe` 启动程序 +2. **连接设备**: 点击"列举设备"查看可用设备,输入connect key后点击"连接设备" +3. **安装应用**: 选择hap文件,设置安装选项后点击"安装hap" +4. **导出文件**: 点击"导出照片"或"导出日志"从设备导出文件 + +## 系统要求 + +- Windows 10/11 +- 无需安装Python或其他依赖 +- 需要HarmonyOS/OpenHarmony设备 + +## 注意事项 + +- 首次使用前请确保设备已开启开发者模式 +- 确保toolchains目录包含hdc.exe文件 +- 导出文件会自动创建时间戳目录并打开文件夹 + +## 故障排除 + +1. **无法连接设备**: 检查设备是否开启USB调试,尝试重启HDC服务 +2. **安装失败**: 检查hap文件是否有效,确认设备存储空间充足 +3. **导出失败**: 确认设备路径正确,检查文件权限 + +--- +基于原C# WPF项目转换的Python Tkinter版本 +""" + + readme_path = os.path.join(dist_dir, "README.md") + with open(readme_path, "w", encoding="utf-8") as f: + f.write(readme_content) + print("已创建发布版README.md") + + + +def clean_build_files(): + """清理构建文件""" + print("清理构建文件...") + + # 清理所有构建相关的临时文件 + dirs_to_clean = ["build", "dist", "__pycache__"] + files_to_clean = ["HarmonyDevTools.spec", "HarmonyDevTools_Enhanced.spec"] + + for dir_name in dirs_to_clean: + if os.path.exists(dir_name): + shutil.rmtree(dir_name) + print(f"已删除目录:{dir_name}") + + for file_name in files_to_clean: + if os.path.exists(file_name): + os.remove(file_name) + print(f"已删除文件:{file_name}") + + print("清理完成") + +def main(): + """主函数""" + print("=" * 50) + print("HarmonyDevTools Python版本打包工具") + print("=" * 50) + + # 检查Python版本 + if sys.version_info < (3, 7): + print("错误:需要Python 3.7或更高版本!") + return + + print(f"Python版本:{sys.version}") + + # 检查PyInstaller + if not check_pyinstaller(): + print("PyInstaller未安装") + choice = input("是否自动安装PyInstaller?(y/n): ").lower() + if choice == 'y': + if not install_pyinstaller(): + return + else: + print("请手动安装PyInstaller:pip install pyinstaller") + return + + # 自动清理之前的构建文件 + if os.path.exists("build") or os.path.exists("dist"): + print("清理之前的构建文件...") + clean_build_files() + + # 构建exe文件 + exe_name = build_exe() + if exe_name: + # 自动创建发布包 + create_distribution(exe_name) + + print("\n构建完成!") + print(f"exe文件位置:dist/{exe_name}.exe") + print("发布包位置:dist/目录") + print("可以直接分发dist目录作为完整的发布包") + else: + print("构建失败!") + +if __name__ == "__main__": + main() diff --git a/HarmonyDevTools_Python/main.py b/HarmonyDevTools_Python/main.py new file mode 100644 index 0000000..dfd9026 --- /dev/null +++ b/HarmonyDevTools_Python/main.py @@ -0,0 +1,668 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +HarmonyDevTools - Python Tkinter版本(增强版) +提供更丰富的功能和更好的用户体验 +""" + +import tkinter as tk +from tkinter import ttk, filedialog, messagebox, scrolledtext, Menu +import subprocess +import os +import threading +import datetime +import logging +import json +import ctypes +import webbrowser +from pathlib import Path +from typing import Optional, Dict, Any + +# --- UI 界面 --- +# 启用 DPI 感知(避免高分屏模糊) +try: + ctypes.windll.shcore.SetProcessDpiAwareness(1) +except Exception: + try: + ctypes.windll.user32.SetProcessDPIAware() + except Exception: + pass + +# 配置日志 +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=[ + logging.FileHandler('harmony_dev_tools.log', encoding='utf-8'), + logging.StreamHandler() + ] +) +logger = logging.getLogger(__name__) + +def center_window(win, width, height): + """让窗口在屏幕中央显示""" + screen_width = win.winfo_screenwidth() + screen_height = win.winfo_screenheight() + x = (screen_width - width) // 2 + y = (screen_height - height) // 2 + win.geometry(f"{width}x{height}+{x}+{y}") + +class Config: + """配置管理类""" + + def __init__(self, config_file="config.json"): + self.config_file = config_file + self.config = self.load_config() + + def load_config(self) -> Dict[str, Any]: + """加载配置""" + default_config = { + "window_size": "970x600", + "theme": "default", + "auto_connect": False, + "default_target": "", + "recent_commands": [], + "max_recent_commands": 10, + "log_level": "INFO" + } + + try: + if os.path.exists(self.config_file): + with open(self.config_file, 'r', encoding='utf-8') as f: + config = json.load(f) + # 合并默认配置 + for key, value in default_config.items(): + if key not in config: + config[key] = value + return config + except Exception as e: + logger.error(f"加载配置失败: {e}") + + return default_config + + def save_config(self): + """保存配置""" + try: + with open(self.config_file, 'w', encoding='utf-8') as f: + json.dump(self.config, f, indent=2, ensure_ascii=False) + except Exception as e: + logger.error(f"保存配置失败: {e}") + + def get(self, key: str, default=None): + """获取配置值""" + return self.config.get(key, default) + + def set(self, key: str, value): + """设置配置值""" + self.config[key] = value + self.save_config() + +class HdcUtil: + """HDC工具类,用于执行hdc命令""" + + def __init__(self): + self.hdc_path = "toolchains/hdc.exe" + self.check_hdc_path() + + def check_hdc_path(self): + """检查hdc.exe路径""" + if not os.path.exists(self.hdc_path): + logger.warning(f"HDC路径不存在: {self.hdc_path}") + # 尝试查找hdc.exe + possible_paths = [ + "hdc.exe", + "toolchains/hdc.exe", + "toolchains\\hdc.exe", + "../toolchains/hdc.exe" + ] + for path in possible_paths: + if os.path.exists(path): + self.hdc_path = path + logger.info(f"找到HDC路径: {self.hdc_path}") + break + + @staticmethod + def run_cmd(parameter="", with_exit=True): + """使用cmd.exe执行命令""" + output = [] + try: + if parameter: + cmd = ["cmd.exe", "/c", parameter] + if with_exit: + cmd.append("&exit") + else: + cmd = ["cmd.exe"] + + process = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + stdin=subprocess.PIPE, + text=True, + creationflags=subprocess.CREATE_NO_WINDOW + ) + + stdout, stderr = process.communicate() + + # 过滤输出 + for line in stdout.split('\n'): + if line and not line.startswith("(c) Microsoft") and not line.startswith("Microsoft Windows"): + output.append(line) + + if stderr: + output.append(f"ERROR: {stderr}") + + except Exception as e: + logger.error(f"执行命令失败: {e}") + output.append(f"ERROR: {e}") + + return '\n'.join(output) + + def run_exe(self, exe_path, arguments=""): + """使用指定的exe文件执行命令""" + output = [] + try: + process = subprocess.Popen( + [exe_path] + arguments.split() if arguments else [exe_path], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + creationflags=subprocess.CREATE_NO_WINDOW + ) + + stdout, stderr = process.communicate() + + if stdout: + output.extend(stdout.split('\n')) + + if stderr: + output.append(f"ERROR: {stderr}") + + except Exception as e: + logger.error(f"执行exe失败: {e}") + output.append(f"ERROR: {e}") + + return '\n'.join(output) + + def export_file(self, origin_path): + """导出文件""" + formatted_date = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + HdcUtil.run_cmd(f"mkdir {formatted_date}") + command = f"file recv {origin_path} ./{formatted_date}" + return self.run_exe(self.hdc_path, command) + + def run_command(self, command): + """运行hdc命令""" + return self.run_exe(self.hdc_path, command) + +class CommandHistory: + """命令历史管理""" + + def __init__(self, max_history=50): + self.max_history = max_history + self.history = [] + self.current_index = -1 + + def add_command(self, command): + """添加命令到历史""" + if command and command not in self.history: + self.history.append(command) + if len(self.history) > self.max_history: + self.history.pop(0) + self.current_index = len(self.history) + + def get_previous(self): + """获取上一条命令""" + if self.history and self.current_index > 0: + self.current_index -= 1 + return self.history[self.current_index] + return "" + + def get_next(self): + """获取下一条命令""" + if self.history and self.current_index < len(self.history) - 1: + self.current_index += 1 + return self.history[self.current_index] + return "" + + def get_recent(self, count=10): + """获取最近的命令""" + return self.history[-count:] if self.history else [] + +class HarmonyDevTools: + """Harmony开发工具主界面(增强版)""" + + def __init__(self, root): + self.root = root + self.config = Config() + self.hdc_util = HdcUtil() + self.command_history = CommandHistory() + + # 设置窗口 + self.setup_window() + self.setup_menu() + self.setup_ui() + self.setup_bindings() + + # 加载配置 + self.load_config() + + def setup_window(self): + """设置窗口""" + self.root.title("HDC Tools - Python版本") + self.root.minsize(970, 600) # 最小大小限制 + self.root.resizable(False, True) # 允许水平 & 垂直拉伸 + center_window(self.root, 970, 600) # 初始化居中 + + # 设置图标(如果存在) + try: + if os.path.exists("icon.ico"): + self.root.iconbitmap("icon.ico") + except: + pass + + def setup_menu(self): + """设置菜单栏""" + menubar = Menu(self.root) + self.root.config(menu=menubar) + + # 文件菜单 + file_menu = Menu(menubar, tearoff=0) + menubar.add_cascade(label="文件", menu=file_menu) + file_menu.add_command(label="保存日志", command=self.save_log) + file_menu.add_separator() + file_menu.add_command(label="退出", command=self.on_closing) + + # 工具菜单 + tools_menu = Menu(menubar, tearoff=0) + menubar.add_cascade(label="工具", menu=tools_menu) + tools_menu.add_command(label="清理HDC进程", command=self.kill_hdc_processes) + tools_menu.add_command(label="检查HDC状态", command=self.check_hdc_status) + tools_menu.add_separator() + tools_menu.add_command(label="打开日志文件", command=self.open_log_file) + + # 帮助菜单 + help_menu = Menu(menubar, tearoff=0) + menubar.add_cascade(label="帮助", menu=help_menu) + help_menu.add_command(label="关于", command=self.show_about) + help_menu.add_command(label="查看帮助", command=self.show_help) + + def setup_ui(self): + """设置用户界面""" + # 主框架 + main_frame = ttk.Frame(self.root, padding="10") + main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S)) + + # 配置网格权重 + self.root.columnconfigure(0, weight=1) + self.root.rowconfigure(0, weight=1) + main_frame.columnconfigure(1, weight=1) + main_frame.columnconfigure(2, weight=1) + + # 第一行:设备操作 + row = 0 + ttk.Button(main_frame, text="列举设备", width=12, command=self.list_devices).grid( + row=row, column=0, padx=(0, 5), pady=5) + + self.target_var = tk.StringVar() + target_entry = ttk.Entry(main_frame, textvariable=self.target_var, width=30) + target_entry.grid(row=row, column=1, columnspan=2, padx=5, pady=5, sticky=(tk.W, tk.E)) + + ttk.Button(main_frame, text="连接设备", width=12, command=self.connect_device).grid( + row=row, column=3, padx=5, pady=5) + + ttk.Button(main_frame, text="重启hdc", width=12, command=self.restart_hdc).grid( + row=row, column=4, padx=5, pady=5) + + ttk.Button(main_frame, text="版本信息", width=12, command=self.check_version).grid( + row=row, column=5, padx=(5, 0), pady=5) + + # 第二行:安装操作 + row += 1 + install_frame = ttk.LabelFrame(main_frame, text="安装操作", padding="5") + install_frame.grid(row=row, column=0, columnspan=3, padx=(0, 5), pady=5, sticky=(tk.W, tk.E)) + + ttk.Button(install_frame, text="安装hap", width=12, command=self.install_hap).pack(side=tk.LEFT) + + self.replace_var = tk.BooleanVar() + ttk.Checkbutton(install_frame, text="替换安装", variable=self.replace_var).pack(side=tk.LEFT, padx=(10, 0)) + + self.downgrade_var = tk.BooleanVar() + ttk.Checkbutton(install_frame, text="允许降级", variable=self.downgrade_var).pack(side=tk.LEFT, padx=(10, 0)) + + self.dynamic_var = tk.BooleanVar() + ttk.Checkbutton(install_frame, text="动态授权", variable=self.dynamic_var).pack(side=tk.LEFT, padx=(10, 0)) + + # 卸载操作 + uninstall_frame = ttk.LabelFrame(main_frame, text="卸载操作", padding="5") + uninstall_frame.grid(row=row, column=3, columnspan=3, padx=(5, 0), pady=5, sticky=(tk.W, tk.E)) + + self.package_name_var = tk.StringVar() + ttk.Entry(uninstall_frame, textvariable=self.package_name_var, width=25).pack(side=tk.LEFT) + + ttk.Button(uninstall_frame, text="卸载应用", width=12, command=self.uninstall_app).pack(side=tk.LEFT, padx=(10, 0)) + + # 第三行:其他操作 + row += 1 + ttk.Button(main_frame, text="导出照片", width=12, command=self.export_photo).grid( + row=row, column=0, padx=(0, 5), pady=5) + + ttk.Button(main_frame, text="UDID", width=12, command=self.get_udid).grid( + row=row, column=1, padx=5, pady=5) + + # 导出日志功能 + self.log_pkg_var = tk.StringVar(value="com.xylink.video.conference") + ttk.Entry(main_frame, textvariable=self.log_pkg_var, width=30).grid( + row=row, column=2, columnspan=2, padx=5, pady=5, sticky=(tk.W, tk.E)) + + ttk.Button(main_frame, text="导出日志", width=12, command=self.export_log).grid( + row=row, column=4, padx=5, pady=5) + + # 第四行:命令执行 + row += 1 + self.command_var = tk.StringVar() + command_entry = ttk.Entry(main_frame, textvariable=self.command_var, width=50) + command_entry.grid(row=row, column=0, columnspan=5, padx=(0, 5), pady=5, sticky=(tk.W, tk.E)) + + ttk.Button(main_frame, text="执行", width=12, command=self.execute_command).grid( + row=row, column=5, padx=(5, 0), pady=5) + + # 第五行:结果显示 + row += 1 + result_frame = ttk.LabelFrame(main_frame, text="执行结果", padding="5") + result_frame.grid(row=row, column=0, columnspan=6, padx=0, pady=5, sticky=(tk.W, tk.E, tk.N, tk.S)) + + # 创建结果显示区域 + self.result_text = scrolledtext.ScrolledText(result_frame, height=15, width=80) + self.result_text.pack(fill=tk.BOTH, expand=True) + + # 配置结果区域网格权重 + result_frame.columnconfigure(0, weight=1) + result_frame.rowconfigure(0, weight=1) + main_frame.rowconfigure(row, weight=1) + + # 保存控件引用 + self.target_entry = target_entry + self.command_entry = command_entry + + def setup_bindings(self): + """设置事件绑定""" + # 命令输入框绑定回车键 + self.command_entry.bind('', lambda e: self.execute_command()) + + # 命令输入框绑定上下箭头键(历史记录) + self.command_entry.bind('', lambda e: self.show_command_history('up')) + self.command_entry.bind('', lambda e: self.show_command_history('down')) + + # 窗口大小改变事件 + self.root.bind('', self.on_window_resize) + + def show_command_history(self, direction): + """显示命令历史""" + if direction == 'up': + command = self.command_history.get_previous() + else: + command = self.command_history.get_next() + + if command: + self.command_var.set(command) + # 选中所有文本 + self.command_entry.select_range(0, tk.END) + + def on_window_resize(self, event): + """窗口大小改变事件""" + if event.widget == self.root: + # 保存窗口大小到配置 + self.config.set("window_size", f"{event.width}x{event.height}") + + def load_config(self): + """加载配置""" + # 加载默认目标 + default_target = self.config.get("default_target", "") + if default_target: + self.target_var.set(default_target) + + def append_result(self, text): + """添加结果到显示区域""" + timestamp = datetime.datetime.now().strftime("%H:%M:%S") + self.result_text.insert(tk.END, f"[{timestamp}] {text}\n") + self.result_text.see(tk.END) + self.root.update_idletasks() + + def execute_command_async(self, command): + """异步执行命令""" + def run(): + self.append_result(f"执行命令: {command}") + result = self.hdc_util.run_command(command) + self.append_result(f"执行结果: {result}") + + # 添加到命令历史 + self.command_history.add_command(command) + + thread = threading.Thread(target=run) + thread.daemon = True + thread.start() + + def list_devices(self): + """列举设备""" + self.command_var.set("list targets -v") + self.execute_command_async("list targets -v") + + def connect_device(self): + """连接设备""" + target = self.target_var.get().strip() + if not target: + messagebox.showwarning("警告", "请输入connect key") + return + command = f"-t {target}" + self.command_var.set(command) + self.execute_command_async(command) + + # 保存到配置 + self.config.set("default_target", target) + + def restart_hdc(self): + """重启hdc""" + self.command_var.set("kill -r") + self.execute_command_async("kill -r") + + def check_version(self): + """检查版本信息""" + self.command_var.set("checkserver") + self.execute_command_async("checkserver") + + def install_hap(self): + """安装hap文件""" + file_path = filedialog.askopenfilename( + title="选择hap文件", + filetypes=[("HarmonyNEXT", "*.hap"), ("所有文件", "*.*")] + ) + + if file_path: + # 统一路径格式,确保使用正确的路径分隔符 + normalized_path = os.path.normpath(file_path) + self.append_result(f"选择文件: {normalized_path}") + + command = "install" + if self.replace_var.get(): + command += " -r" + if self.downgrade_var.get(): + command += " -d" + if self.dynamic_var.get(): + command += " -g" + + command += f" \"{normalized_path}\"" + self.command_var.set(command) + self.execute_command_async(command) + + def uninstall_app(self): + """卸载应用""" + package_name = self.package_name_var.get().strip() + if not package_name: + messagebox.showwarning("警告", "请输入包名") + return + command = f"uninstall {package_name}" + self.command_var.set(command) + self.execute_command_async(command) + + def export_photo(self): + """导出照片""" + self.export_file("/storage/media/100/local/files/Photo") + + def export_file(self, path): + """导出文件""" + def run(): + formatted_date = datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S") + HdcUtil.run_cmd(f"mkdir {formatted_date}") + self.append_result(f"创建目录: {formatted_date}") + self.append_result(f"导出 {path} 到 {formatted_date}") + result = self.hdc_util.export_file(path) + self.append_result(f"导出结果: {result}") + + # 导出完成后打开文件夹 + try: + if os.path.exists(formatted_date): + os.startfile(formatted_date) + self.append_result(f"已打开导出目录: {formatted_date}") + except Exception as e: + self.append_result(f"打开文件夹失败: {e}") + + thread = threading.Thread(target=run) + thread.daemon = True + thread.start() + + def get_udid(self): + """获取UDID""" + self.command_var.set("shell bm get --udid") + self.execute_command_async("shell bm get --udid") + + def export_log(self): + """导出日志""" + package_name = self.log_pkg_var.get().strip() + if not package_name: + messagebox.showwarning("警告", "请输入包名") + return + path = f"/data/app/el2/100/base/{package_name}/haps/entry/files" + self.export_file(path) + + def execute_command(self): + """执行命令""" + command = self.command_var.get().strip() + if not command: + messagebox.showwarning("警告", "请输入命令") + return + self.execute_command_async(command) + + def save_log(self): + """保存日志""" + file_path = filedialog.asksaveasfilename( + title="保存日志", + defaultextension=".txt", + filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")] + ) + + if file_path: + try: + with open(file_path, 'w', encoding='utf-8') as f: + f.write(self.result_text.get(1.0, tk.END)) + messagebox.showinfo("成功", f"日志已保存到: {file_path}") + except Exception as e: + messagebox.showerror("错误", f"保存失败: {e}") + + def kill_hdc_processes(self): + """清理HDC进程""" + def run(): + result = HdcUtil.run_cmd("taskkill /IM hdc.exe") + self.append_result(f"清理HDC进程: {result}") + + thread = threading.Thread(target=run) + thread.daemon = True + thread.start() + + def check_hdc_status(self): + """检查HDC状态""" + self.execute_command_async("checkserver") + + def open_log_file(self): + """打开日志文件""" + log_file = "harmony_dev_tools.log" + if os.path.exists(log_file): + try: + os.startfile(log_file) + except: + messagebox.showinfo("信息", f"日志文件位置: {os.path.abspath(log_file)}") + else: + messagebox.showinfo("信息", "暂无日志文件") + + def show_about(self): + """显示关于信息""" + about_text = """HarmonyDevTools - Python版本 + +版本: 1.0.0 +作者: Python版本转换 +功能: HarmonyOS/OpenHarmony开发工具 + +基于原C# WPF项目转换而来, +使用Python Tkinter重新实现。 + +支持功能: +- 设备管理 +- 应用安装/卸载 +- 文件导出 +- 命令执行 +""" + messagebox.showinfo("关于", about_text) + + def show_help(self): + """显示帮助""" + help_text = """使用说明: + +1. 设备连接: + - 点击"列举设备"查看可用设备 + - 输入connect key后点击"连接设备" + +2. 应用管理: + - 选择hap文件进行安装 + - 输入包名进行卸载 + +3. 文件操作: + - 点击"导出照片"导出设备照片 + - 点击"导出日志"导出指定应用的日志文件 + - 使用自定义命令执行其他操作 + +4. 快捷键: + - 命令输入框支持上下箭头键浏览历史 + - 回车键执行命令 + +5. 日志管理: + - 可通过菜单保存执行日志 + - 日志文件自动保存在程序目录 +""" + messagebox.showinfo("帮助", help_text) + + def on_closing(self): + """窗口关闭时的处理""" + try: + # 保存配置 + self.config.save_config() + + # 清理HDC进程 + result = HdcUtil.run_cmd("taskkill /IM hdc.exe") + self.append_result(result) + except: + pass + self.root.destroy() + +def main(): + """主函数""" + root = tk.Tk() + app = HarmonyDevTools(root) + + # 绑定关闭事件 + root.protocol("WM_DELETE_WINDOW", app.on_closing) + + # 启动应用 + root.mainloop() + +if __name__ == "__main__": + main() diff --git a/HarmonyDevTools_Python/requirements.txt b/HarmonyDevTools_Python/requirements.txt new file mode 100644 index 0000000..dc6ee8a --- /dev/null +++ b/HarmonyDevTools_Python/requirements.txt @@ -0,0 +1,14 @@ +# Python Tkinter项目依赖 +# 注意:tkinter是Python标准库的一部分,通常不需要额外安装 +# 但为了确保项目完整性,这里列出所有可能的依赖 + +# 如果需要额外的GUI增强功能,可以添加以下依赖: +# tkinter-tooltip==2.0.0 # 工具提示功能 +# tkinterdnd2==0.3.0 # 拖拽功能 + +# 日志相关(可选) +# colorlog==6.7.0 # 彩色日志输出 + +# 打包相关(用于生成exe文件) +# pyinstaller==5.13.0 # 打包为exe文件 +# auto-py-to-exe==2.32.0 # GUI打包工具 diff --git a/HarmonyDevTools_Python/run.bat b/HarmonyDevTools_Python/run.bat new file mode 100644 index 0000000..e1955ec --- /dev/null +++ b/HarmonyDevTools_Python/run.bat @@ -0,0 +1,37 @@ +@echo off +chcp 65001 >nul +echo ======================================== +echo HarmonyDevTools Python版本启动脚本 +echo ======================================== + + +REM 检查Python是否安装 +python --version >nul 2>&1 +if errorlevel 1 ( + echo 错误:未找到Python,请先安装Python 3.7或更高版本 + echo 下载地址:https://www.python.org/downloads/ + pause + exit /b 1 +) + +REM 检查主程序文件是否存在 +if not exist "main.py" ( + echo 错误:找不到main.py文件! + pause + exit /b 1 +) + +REM 检查toolchains目录 +if not exist "toolchains" ( + echo 警告:找不到toolchains目录,请确保包含hdc.exe文件 + echo. +) + +echo 正在启动HarmonyDevTools... +echo. + +python main.py + +echo. +echo 程序已退出 +pause diff --git a/HarmonyDevTools_Python/toolchains/hdc.exe b/HarmonyDevTools_Python/toolchains/hdc.exe new file mode 100644 index 0000000..34bb770 Binary files /dev/null and b/HarmonyDevTools_Python/toolchains/hdc.exe differ diff --git a/HarmonyDevTools_Python/toolchains/libusb_shared.dll b/HarmonyDevTools_Python/toolchains/libusb_shared.dll new file mode 100644 index 0000000..fd659af Binary files /dev/null and b/HarmonyDevTools_Python/toolchains/libusb_shared.dll differ