diff --git a/.gitignore b/.gitignore
index 49f9515..c5bd364 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,108 @@
-/.idea
-*/obj
-*/bin
-*/pack/output
\ No newline at end of file
+# 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]/
+
+.idea/
diff --git a/HarmonyDevTools.sln b/HarmonyDevTools.sln
deleted file mode 100644
index e020f82..0000000
--- a/HarmonyDevTools.sln
+++ /dev/null
@@ -1,16 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HarmonyDevTools", "HarmonyDevTools\HarmonyDevTools.csproj", "{BBCBF001-B0B9-4F6D-A6EF-CC85E9D6FFFB}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Release|Any CPU = Release|Any CPU
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {BBCBF001-B0B9-4F6D-A6EF-CC85E9D6FFFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {BBCBF001-B0B9-4F6D-A6EF-CC85E9D6FFFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {BBCBF001-B0B9-4F6D-A6EF-CC85E9D6FFFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {BBCBF001-B0B9-4F6D-A6EF-CC85E9D6FFFB}.Release|Any CPU.Build.0 = Release|Any CPU
- EndGlobalSection
-EndGlobal
diff --git a/HarmonyDevTools.sln.DotSettings.user b/HarmonyDevTools.sln.DotSettings.user
deleted file mode 100644
index 4734008..0000000
--- a/HarmonyDevTools.sln.DotSettings.user
+++ /dev/null
@@ -1,3 +0,0 @@
-
- True
- SOLUTION
\ No newline at end of file
diff --git a/HarmonyDevTools/App.xaml b/HarmonyDevTools/App.xaml
deleted file mode 100644
index 834173e..0000000
--- a/HarmonyDevTools/App.xaml
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
diff --git a/HarmonyDevTools/App.xaml.cs b/HarmonyDevTools/App.xaml.cs
deleted file mode 100644
index dc5d6ec..0000000
--- a/HarmonyDevTools/App.xaml.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Configuration;
-using System.Data;
-using System.Linq;
-using System.Threading.Tasks;
-using System.Windows;
-
-namespace HarmonyDevTools
-{
- ///
- /// Interaction logic for App.xaml
- ///
- public partial class App : Application
- {
-
- }
-}
\ No newline at end of file
diff --git a/HarmonyDevTools/AssemblyInfo.cs b/HarmonyDevTools/AssemblyInfo.cs
deleted file mode 100644
index 4a05c7d..0000000
--- a/HarmonyDevTools/AssemblyInfo.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using System.Windows;
-
-[assembly: ThemeInfo(
- ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
- //(used if a resource is not found in the page,
- // or application resource dictionaries)
- ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
- //(used if a resource is not found in the page,
- // app, or any theme specific resource dictionaries)
-)]
\ No newline at end of file
diff --git a/HarmonyDevTools/HarmonyDevTools.csproj b/HarmonyDevTools/HarmonyDevTools.csproj
deleted file mode 100644
index 7ed6cdf..0000000
--- a/HarmonyDevTools/HarmonyDevTools.csproj
+++ /dev/null
@@ -1,31 +0,0 @@
-
-
-
- WinExe
- net6.0-windows
- enable
- true
- icon.ico
- HarmonyDevTools
- DevWiki
- OpenHarmony and HarmonyOS Develop Tools
- DevWiki
- icon.ico
- OpenHarmony,HarmonyOS
- 1.0.2
- 1.0.2
- 1.0.2
-
-
-
-
- ..\libs\log4net.dll
-
-
-
-
-
-
-
-
-
diff --git a/HarmonyDevTools/MainWindow.xaml b/HarmonyDevTools/MainWindow.xaml
deleted file mode 100644
index 26c037a..0000000
--- a/HarmonyDevTools/MainWindow.xaml
+++ /dev/null
@@ -1,76 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/HarmonyDevTools/MainWindow.xaml.cs b/HarmonyDevTools/MainWindow.xaml.cs
deleted file mode 100644
index 460f20e..0000000
--- a/HarmonyDevTools/MainWindow.xaml.cs
+++ /dev/null
@@ -1,136 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Threading.Tasks;
-using System.Windows;
-using HarmonyDevTools.util;
-using Microsoft.Win32;
-
-namespace HarmonyDevTools;
-
-///
-/// Interaction logic for MainWindow.xaml
-///
-public partial class MainWindow : Window
-{
- public MainWindow()
- {
- InitializeComponent();
- }
-
- private void InstallOnClick(object sender, RoutedEventArgs e)
- {
- OpenFileDialog dialog = new OpenFileDialog()
- {
- Title = "Select a package",
- Filter = "HarmonyNEXT(*.hap)|*.hap",
- FilterIndex = 1,
- RestoreDirectory = true
- };
- if (dialog.ShowDialog() == true)
- {
- // 获取所选文件的路径并显示在文本框中
- string filePath = dialog.FileName;
- AppendHintAndScrollToEnd($"select file: {filePath} \n");
- string command = "install";
- if (ReplaceCb.IsChecked ?? false)
- {
- command += " -r";
- }
-
- if (DowngradeCb.IsChecked ?? false)
- {
- command += " -d";
- }
-
- if (DynamicCb.IsChecked ?? false)
- {
- command += " -g";
- }
- CommandTb.Text = $"{command} {filePath}";
- GetCommandAndExecute();
- }
- }
-
- private void CommandBtn_OnClick(object sender, RoutedEventArgs e)
- {
- GetCommandAndExecute();
- }
-
- private void ListBtn_OnClick(object sender, RoutedEventArgs e)
- {
- CommandTb.Text = "list targets -v";
- GetCommandAndExecute();
- }
-
- private async void GetCommandAndExecute()
- {
- string command = CommandTb.Text;
- AppendHintAndScrollToEnd( $"execute command: {command} \n");
- string result = await Task.Run(() => HdcUtil.RunCommand(command));
- AppendHintAndScrollToEnd($"execute result: {result} \n");
- }
-
- private void CheckServerBtn_OnClick(object sender, RoutedEventArgs e)
- {
- CommandTb.Text = "checkserver";
- GetCommandAndExecute();
- }
-
- private void ConnectBtn_OnClick(object sender, RoutedEventArgs e)
- {
- CommandTb.Text = $"-t {TargetTb.Text}";
- GetCommandAndExecute();
- }
-
- private void KillBtn_OnClick(object sender, RoutedEventArgs e)
- {
- CommandTb.Text = "kill -r";
- GetCommandAndExecute();
- }
-
- protected override void OnClosing(CancelEventArgs e)
- {
- e.Cancel = true;
- KillHdcExe();
- e.Cancel = false;
- }
-
- private async void KillHdcExe()
- {
- string result = await Task.Run(() => CmdUtil.RunCmd("taskkill /IM hdc.exe"));
- AppendHintAndScrollToEnd(result + "\n");
- }
-
- private void UninstallBtn_OnClick(object sender, RoutedEventArgs e)
- {
- CommandTb.Text = $"uninstall {PackageNameTb.Text}";
- GetCommandAndExecute();
- }
-
- private void AppendHintAndScrollToEnd(string hint)
- {
- ResultTb.Text += hint;
- ResultTb.ScrollToEnd();
- }
-
- private void GetPhoto_OnClick(object sender, RoutedEventArgs e)
- {
- ExportFile("/storage/media/100/local/files/Photo");
- }
-
- private async void ExportFile(string path)
- {
- var formattedDate = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss");
- CmdUtil.RunCmd($"mkdir {formattedDate}");
- AppendHintAndScrollToEnd($"create dir: {formattedDate} \n");
- AppendHintAndScrollToEnd($"export {path} to {formattedDate} \n");
- string result = await Task.Run(() => HdcUtil.ExportFile(path));
- AppendHintAndScrollToEnd($"export result: {result} \n");
- }
-
- private void GetUDID_OnClick(object sender, RoutedEventArgs e)
- {
- CommandTb.Text = "shell bm get --udid";
- GetCommandAndExecute();
- }
-}
\ No newline at end of file
diff --git a/HarmonyDevTools/control/TextBoxHelper.cs b/HarmonyDevTools/control/TextBoxHelper.cs
deleted file mode 100644
index a8fe796..0000000
--- a/HarmonyDevTools/control/TextBoxHelper.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Media;
-
-namespace HarmonyDevTools.control;
-
-public static class TextBoxHelper
-{
- public static readonly DependencyProperty PlaceholderProperty =
- DependencyProperty.RegisterAttached("Placeholder", typeof(string), typeof(TextBoxHelper),
- new PropertyMetadata(default(string), OnPlaceholderChanged));
-
- public static void SetPlaceholder(UIElement element, string value)
- {
- element.SetValue(PlaceholderProperty, value);
- }
-
- public static string GetPlaceholder(UIElement element)
- {
- return (string)element.GetValue(PlaceholderProperty);
- }
-
- private static void OnPlaceholderChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
- {
- if (d is TextBox textBox)
- {
- textBox.GotFocus -= RemovePlaceholder;
- textBox.LostFocus -= ShowPlaceholder;
-
- if (!string.IsNullOrEmpty((string)e.NewValue))
- {
- textBox.GotFocus += RemovePlaceholder;
- textBox.LostFocus += ShowPlaceholder;
-
- ShowPlaceholder(textBox, null);
- }
- }
- }
-
- private static void RemovePlaceholder(object sender, RoutedEventArgs e)
- {
- if (sender is TextBox textBox && textBox.Text == GetPlaceholder(textBox))
- {
- textBox.Text = "";
- textBox.Foreground = Brushes.Black;
- }
- }
-
- private static void ShowPlaceholder(object sender, RoutedEventArgs e)
- {
- if (sender is TextBox textBox && string.IsNullOrEmpty(textBox.Text))
- {
- textBox.Text = GetPlaceholder(textBox);
- textBox.Foreground = Brushes.Gray;
- }
- }
-}
\ No newline at end of file
diff --git a/HarmonyDevTools/icon.ico b/HarmonyDevTools/icon.ico
deleted file mode 100644
index 79aa53b..0000000
Binary files a/HarmonyDevTools/icon.ico and /dev/null differ
diff --git a/HarmonyDevTools/pack/build.bat b/HarmonyDevTools/pack/build.bat
deleted file mode 100644
index fc0a1a8..0000000
--- a/HarmonyDevTools/pack/build.bat
+++ /dev/null
@@ -1,31 +0,0 @@
-set version=1.0.1
-
-if not "%~1"=="" (
- set version=%1
-)
-
-cd ../ && rmdir /s /q bin && rmdir /s /q obj
-cd pack
-
-if not exist "output" (
- mkdir output
-)
-
-cd output
-if exist "HarmonyTools-%version%.exe" (
- del /q /s HarmonyDevTools-%version%.exe
-)
-cd ../
-
-dotnet publish ../../HarmonyDevTools/HarmonyDevTools.csproj -c Release -r win-x86 /p:PublishSingleFile=true /p:IncludeNativeLibrariesForSelfExtract=true /p:SelfContained=false -o ./output
-
-if NOT %errorlevel%==0 @goto :FailureOnBuild
-
-cd output && move HarmonyDevTools.exe HarmonyDevTools-%version%.exe && move HarmonyDevTools.pdb HarmonyDevTools-%version%.pdb && cd ../
-xcopy ..\..\toolchains .\output\toolchains /E /I /Y
-cd ../bin && del /q /s Release && cd ../pack
-
-goto :eof
-
-:FailureOnBuild
-echo build project failure
\ No newline at end of file
diff --git a/HarmonyDevTools/util/CmdUtil.cs b/HarmonyDevTools/util/CmdUtil.cs
deleted file mode 100644
index 1fa8a05..0000000
--- a/HarmonyDevTools/util/CmdUtil.cs
+++ /dev/null
@@ -1,119 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Text;
-using log4net;
-
-namespace HarmonyDevTools.util;
-
-public static class CmdUtil
-{
- private static readonly ILog Log = LogManager.GetLogger(nameof(CmdUtil));
- private const string CmdExe = "cmd.exe";
-
- /**
- * 使用 cmd.exe 执行
- */
- public static string RunCmd(string parameter = "", bool withExit = true)
- {
- var output = new StringBuilder();
- try
- {
- var pro = new Process
- {
- StartInfo =
- {
- FileName = CmdExe,
- UseShellExecute = false,
- RedirectStandardInput = true,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- CreateNoWindow = true
- }
- };
- if (parameter.Length > 0)
- {
- pro.StartInfo.Arguments = parameter;
- }
-
- pro.Start();
-
- if (parameter.Length > 0)
- {
- pro.StandardInput.WriteLine(parameter + (withExit ? " &exit" : ""));
- pro.StandardInput.AutoFlush = true;
- }
-
- while (!pro.StandardOutput.EndOfStream)
- {
- string? line = pro.StandardOutput.ReadLine();
- if (!string.IsNullOrEmpty(line) && !line.StartsWith("(c) Microsoft") && !line.StartsWith("Microsoft Windows"))
- {
- output.AppendLine(line);
- }
- }
-
- if (withExit)
- {
- pro.WaitForExit();
- pro.Close();
- }
- }
- catch (Exception e)
- {
- Log.Error(e);
- }
- return output.ToString();
- }
-
- /**
- * 使用指定的 exe 文件执行
- */
- public static string RunExe(string exePath, string arguments = "")
- {
- var output = new StringBuilder();
- try
- {
- var process = new Process
- {
- StartInfo = new ProcessStartInfo
- {
- FileName = exePath,
- Arguments = arguments,
- UseShellExecute = false,
- RedirectStandardInput = true,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- CreateNoWindow = true
- }
- };
-
- process.OutputDataReceived += (sender, args) =>
- {
- if (!string.IsNullOrEmpty(args.Data))
- {
- output.AppendLine(args.Data);
- }
- };
-
- process.ErrorDataReceived += (sender, args) =>
- {
- if (!string.IsNullOrEmpty(args.Data))
- {
- output.AppendLine($"ERROR: {args.Data}");
- }
- };
-
- process.Start();
- process.BeginOutputReadLine();
- process.BeginErrorReadLine();
-
- process.WaitForExit();
- process.Close();
- }
- catch (Exception e)
- {
- Log.Error(e);
- }
- return output.ToString();
- }
-}
\ No newline at end of file
diff --git a/HarmonyDevTools/util/HdcUtil.cs b/HarmonyDevTools/util/HdcUtil.cs
deleted file mode 100644
index 970fdd5..0000000
--- a/HarmonyDevTools/util/HdcUtil.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using System;
-
-namespace HarmonyDevTools.util;
-
-public static class HdcUtil
-{
- public static string ExportFile(string originPath)
- {
- var formattedDate = DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss");
- CmdUtil.RunCmd($"mkdir {formattedDate}");
- var command = $"file recv {originPath} ./{formattedDate}";
- return CmdUtil.RunExe("toolchains\\hdc.exe", command);
- }
-
- public static string RunCommand(string command)
- {
- return CmdUtil.RunExe("toolchains\\hdc.exe", command);
- }
-}
\ No newline at end of file
diff --git a/HarmonyDevTools_v1.4.0.zip b/HarmonyDevTools_v1.4.0.zip
new file mode 100644
index 0000000..5ed5113
Binary files /dev/null and b/HarmonyDevTools_v1.4.0.zip differ
diff --git a/build.py b/build.py
new file mode 100644
index 0000000..bb81899
--- /dev/null
+++ b/build.py
@@ -0,0 +1,323 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+HarmonyDevTools Python版本打包脚本
+用于将Python项目打包为exe文件
+"""
+
+import os
+import sys
+import subprocess
+import shutil
+import re
+import zipfile
+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 get_version_from_version_info():
+ """从version_info.txt文件中获取版本号"""
+ if not os.path.exists("version_info.txt"):
+ return "1.0.0"
+
+ try:
+ with open("version_info.txt", 'r', encoding='utf-8') as f:
+ content = f.read()
+ match = re.search(r"StringStruct\(u'FileVersion', u'([^']+)'\)", content)
+ if match:
+ return match.group(1)
+ except Exception as e:
+ print(f"读取version_info.txt版本信息失败: {e}")
+
+ return "1.0.0"
+
+def build_with_version(main_file, exe_name, version):
+ """使用指定版本号构建exe文件"""
+ # 构建命令
+ cmd = [
+ "pyinstaller",
+ "--onefile", # 打包为单个文件
+ "--windowed", # 无控制台窗口
+ f"--name={exe_name}", # 输出文件名
+ "--icon=icon.ico", # 图标文件(如果存在)
+ "--version-file=version_info.txt", # 版本信息文件
+ "--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")]
+
+ # 如果版本信息文件不存在,移除版本信息参数
+ if not os.path.exists("version_info.txt"):
+ cmd = [arg for arg in cmd if not arg.startswith("--version-file")]
+
+ return cmd
+
+def get_version_for_filename(version):
+ """将版本号转换为文件名格式(只取前三位)"""
+ parts = version.split('.')
+ if len(parts) >= 3:
+ return f"{parts[0]}.{parts[1]}.{parts[2]}"
+ return version
+
+def build_exe():
+ """构建exe文件"""
+ print("开始构建exe文件...")
+
+ # 检查主程序文件是否存在
+ if not os.path.exists("main.py"):
+ print("错误:找不到main.py文件!")
+ return False
+
+ # 从version_info.txt获取版本号
+ version = get_version_from_version_info()
+ version_for_filename = get_version_for_filename(version)
+ main_file = "main.py"
+ exe_name = f"HarmonyDevTools_v{version_for_filename}"
+ print(f"构建HarmonyDevTools v{version}...")
+
+ # 检查toolchains目录是否存在
+ if not os.path.exists("toolchains"):
+ print("警告:找不到toolchains目录,请确保包含hdc.exe文件")
+
+ # 构建命令
+ cmd = build_with_version(main_file, exe_name, version_for_filename)
+
+ 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文件"""
+ # 获取当前版本号
+ version = get_version_from_version_info()
+ version_for_filename = get_version_for_filename(version)
+ version_underscore = version_for_filename.replace('.', '_')
+
+ readme_content = f"""# HarmonyDevTools
+
+HarmonyOS/OpenHarmony开发工具,提供图形化界面操作HDC工具。
+
+## 功能特性
+
+- **设备管理**: 列举设备、连接设备、重启HDC服务
+- **应用管理**: 安装/卸载HarmonyOS应用包
+- **文件操作**: 导出设备照片、导出应用日志
+- **系统信息**: 获取设备UDID、查看版本信息
+- **命令执行**: 支持执行任意HDC命令
+
+## 使用方法
+
+1. **启动程序**: 双击 `HarmonyDevTools_v{version_underscore}.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版本
+版本: {version}
+"""
+
+ 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 create_zip_package(exe_name, dist_dir):
+ """创建zip压缩包"""
+ print("创建zip压缩包...")
+
+ # 获取版本号用于zip文件名
+ version = get_version_from_version_info()
+ version_for_filename = get_version_for_filename(version)
+ zip_filename = f"HarmonyDevTools_v{version_for_filename}.zip"
+ zip_path = os.path.join(dist_dir, zip_filename)
+
+ # 创建zip文件
+ with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
+ # 添加exe文件到HarmonyDevTools_v1.4.0目录下
+ exe_path = os.path.join(dist_dir, f"{exe_name}.exe")
+ if os.path.exists(exe_path):
+ zipf.write(exe_path, f"HarmonyDevTools_v{version_for_filename}/{exe_name}.exe")
+ print(f"已添加 {exe_name}.exe 到压缩包")
+
+ # 添加toolchains目录下的文件到HarmonyDevTools_v1.4.0/toolchains目录
+ toolchains_dir = os.path.join(dist_dir, "toolchains")
+ if os.path.exists(toolchains_dir):
+ for root, dirs, files in os.walk(toolchains_dir):
+ for file in files:
+ file_path = os.path.join(root, file)
+ # 计算相对路径,去掉dist前缀
+ rel_path = os.path.relpath(file_path, dist_dir)
+ # 在zip中创建toolchains子目录
+ arcname = f"HarmonyDevTools_v{version_for_filename}/{rel_path}"
+ zipf.write(file_path, arcname)
+ print(f"已添加 {arcname} 到压缩包")
+
+ # 添加README.md到HarmonyDevTools_v1.4.0目录下
+ readme_path = os.path.join(dist_dir, "README.md")
+ if os.path.exists(readme_path):
+ zipf.write(readme_path, f"HarmonyDevTools_v{version_for_filename}/README.md")
+ print("已添加 README.md 到压缩包")
+
+ print(f"zip压缩包创建成功:{zip_path}")
+ return zip_filename
+
+
+def clean_build_files():
+ """清理构建文件"""
+ print("清理构建文件...")
+
+ # 清理所有构建相关的临时文件
+ dirs_to_clean = ["build", "__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 clean_after_build(exe_name):
+ """构建完成后清理临时文件"""
+ print("清理构建临时文件...")
+
+ # 清理build目录
+ if os.path.exists("build"):
+ shutil.rmtree("build")
+ print("已删除build目录")
+
+ # 清理spec文件
+ spec_files = [f"{exe_name}.spec"]
+ for spec_file in spec_files:
+ if os.path.exists(spec_file):
+ os.remove(spec_file)
+ print(f"已删除文件:{spec_file}")
+
+ 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)
+
+ # 创建zip压缩包
+ zip_filename = create_zip_package(exe_name, "dist")
+
+ # 构建完成后清理临时文件
+ clean_after_build(exe_name)
+
+ print("\n构建完成!")
+ print(f"exe文件位置:dist/{exe_name}.exe")
+ print(f"zip压缩包:{zip_filename}")
+ print("发布包位置:dist/目录")
+ print("可以直接分发dist目录或zip压缩包作为完整的发布包")
+ else:
+ print("构建失败!")
+
+if __name__ == "__main__":
+ main()
diff --git a/global.json b/global.json
deleted file mode 100644
index 1bcf6c0..0000000
--- a/global.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "sdk": {
- "version": "6.0.0",
- "rollForward": "latestMinor",
- "allowPrerelease": false
- }
-}
\ No newline at end of file
diff --git a/icon.ico b/icon.ico
new file mode 100644
index 0000000..ea37a22
Binary files /dev/null and b/icon.ico differ
diff --git a/libs/log4net.dll b/libs/log4net.dll
deleted file mode 100644
index 79b27e2..0000000
Binary files a/libs/log4net.dll and /dev/null differ
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..4a4e949
--- /dev/null
+++ b/main.py
@@ -0,0 +1,664 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+HarmonyDevTools
+提供更丰富的功能和更好的用户体验
+"""
+
+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
+import re
+from pathlib import Path
+from typing import Optional, Dict, Any
+
+# 版本信息
+VERSION = "1.4.0"
+
+# --- 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}")
+
+def get_version():
+ """获取版本号"""
+ import sys
+ # 检查是否有命令行参数传入版本号
+ if len(sys.argv) > 1 and sys.argv[1].startswith('--version='):
+ return sys.argv[1].split('=', 1)[1]
+ return VERSION
+
+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")
+ 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)
+
+ # 第四行:命令执行
+ 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 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):
+ """显示关于信息"""
+ version = get_version()
+ about_text = f"""HarmonyDevTools
+
+版本: {version}
+作者: DevWiki
+功能: 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/readme.md b/readme.md
index be41a31..fa9251f 100644
--- a/readme.md
+++ b/readme.md
@@ -1,14 +1,15 @@
+# HarmonyDevTools - Python版本



-
+
-本项目为HarmonyOS/OpenHarmony的 toolchains 工具提供UI操作, 方便使用.
+本项目为HarmonyOS/OpenHarmony的 toolchains 工具提供Python Tkinter UI操作界面,方便使用。
## 1. 版本说明
-目前支持 hdc 工具的使用,后续会陆续增加其他工具,
+这是原C# WPF项目的Python Tkinter版本,保持了相同的功能特性:
hdc功能支持:
- 枚举设备/连接设备
@@ -19,34 +20,183 @@ hdc功能支持:
- 获取手机的UDID
- 其他命令的执行(手动输入执行)
-下载地址: **Release** 页面下载最新版本。
+## 2. 环境要求
-
+- Python 3.7+
+- Windows操作系统
+- HarmonyOS/OpenHarmony SDK(包含toolchains目录)
-## 2. 使用方式
+## 3. 安装和运行
-下载最新版本, 解压如下目录, 双击 `HarmonyDevTools.exe` 运行。
+### 3.1 直接运行
-
+1. 确保已安装Python 3.7或更高版本
+2. 下载项目文件
+3. 确保`toolchains`目录包含`hdc.exe`文件
+4. 运行主程序:
+**方式一:使用启动脚本(推荐)**
+```bash
+run.bat
+```
-运行环境要求: netcore6.0, 如果双击无法运行,请下载安装 netcore6.0 runtime:
-- x64版本:https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.31-windows-x64-installer
-- x86版本:https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.31-windows-x86-installer
+**方式二:直接运行Python文件**
+```bash
+python main.py
+```
-## 3. 更新hdc
+### 3.2 安装依赖(可选)
-根据OpenHarmony官方的文档: [hdc使用指导](https://docs.openharmony.cn/pages/v4.1/zh-cn/device-dev/subsystems/subsys-toolchain-hdc-guide.md),
-安装DevEco以后, 设置SDK路径, 下载最新的SDK, 里面包含 toolchains
+如果需要额外的功能,可以安装可选依赖:
-> ## 环境准备
-> hdc 工具获取方式:
->
-> 通过OpenHarmony sdk获取,hdc在sdk的toolchains目录下。
->
-> **使用举例:**
->
-> 下面以windows侧使用方式举例:
->
-> 获取windows的sdk,将hdc.exe放到磁盘某个位置即可使用。
+```bash
+pip install -r requirements.txt
+```
+## 4. 打包为exe文件
+
+### 4.1 使用项目自带的构建脚本(推荐)
+
+```bash
+# 运行构建脚本
+python build.py
+```
+
+构建脚本会:
+- 自动检查PyInstaller是否安装
+- 自动清理之前的构建文件
+- 构建HarmonyDevTools_v版本号.exe(包含版本信息)
+- 自动创建完整的发布包
+- 生成说明文档
+- 构建完成后自动清理临时文件
+
+### 4.2 使用PyInstaller手动打包
+
+```bash
+# 安装PyInstaller
+pip install pyinstaller
+
+# 打包为exe文件
+pyinstaller --onefile --windowed --name HarmonyDevTools_v版本号 main.py
+```
+
+### 4.3 版本管理
+
+```bash
+# 更新版本号
+python update_version.py
+
+# 查看当前版本
+# 运行 update_version.py 会显示当前版本
+```
+
+**版本号格式**: x.y.z (例如: 1.4.0)
+- 文件名格式: HarmonyDevTools_v1_4_0.exe
+- 程序内显示: 1.4.0(构建时通过命令行参数传递)
+- 版本号来源: version_info.txt(main.py中的VERSION变量用于开发调试)
+
+### 4.4 使用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 # 主程序文件
+├── build.py # 构建脚本
+├── version_info.txt # 版本信息文件
+├── update_version.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/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..dc6ee8a
--- /dev/null
+++ b/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/run.bat b/run.bat
new file mode 100644
index 0000000..e1955ec
--- /dev/null
+++ b/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/update_version.py b/update_version.py
new file mode 100644
index 0000000..6c679ce
--- /dev/null
+++ b/update_version.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+版本信息更新脚本
+用于快速更新版本号
+"""
+
+import re
+import os
+
+def update_version_info(new_version):
+ """更新版本信息文件"""
+ version_file = "version_info.txt"
+
+ if not os.path.exists(version_file):
+ print(f"错误:找不到 {version_file} 文件")
+ return False
+
+ # 解析版本号
+ version_parts = new_version.split('.')
+ if len(version_parts) < 3:
+ print("错误:版本号格式应为 x.y.z (例如: 1.4.0)")
+ return False
+
+ try:
+ major, minor, patch = map(int, version_parts[:3])
+ build = 0 # 第四位默认为0
+ except ValueError:
+ print("错误:版本号必须为数字")
+ return False
+
+ # 读取文件内容
+ with open(version_file, 'r', encoding='utf-8') as f:
+ content = f.read()
+
+ # 更新版本号
+ content = re.sub(r'filevers=\(\d+, \d+, \d+, \d+\)',
+ f'filevers=({major}, {minor}, {patch}, {build})', content)
+ content = re.sub(r'prodvers=\(\d+, \d+, \d+, \d+\)',
+ f'prodvers=({major}, {minor}, {patch}, {build})', content)
+ content = re.sub(r"StringStruct\(u'FileVersion', u'[^']+'\)",
+ f"StringStruct(u'FileVersion', u'{new_version}')", content)
+ content = re.sub(r"StringStruct\(u'ProductVersion', u'[^']+'\)",
+ f"StringStruct(u'ProductVersion', u'{new_version}')", content)
+
+ # 写回文件
+ with open(version_file, 'w', encoding='utf-8') as f:
+ f.write(content)
+
+ print(f"版本信息已更新为: {new_version}")
+ print(f"下次构建将生成: HarmonyDevTools_v{new_version.replace('.', '_')}.exe")
+ print("注意:构建时会通过命令行参数传递版本号,main.py中的VERSION变量保持不变")
+ return True
+
+def main():
+ """主函数"""
+ print("=" * 50)
+ print("HarmonyDevTools 版本更新工具")
+ print("=" * 50)
+
+ # 显示当前版本
+ if os.path.exists("version_info.txt"):
+ with open("version_info.txt", 'r', encoding='utf-8') as f:
+ content = f.read()
+ match = re.search(r"StringStruct\(u'FileVersion', u'([^']+)'\)", content)
+ if match:
+ current_version = match.group(1)
+ print(f"当前版本: {current_version}")
+
+ # 获取新版本号
+ print("\n请输入新版本号 (格式: x.y.z,例如: 1.4.0)")
+ new_version = input("新版本号: ").strip()
+
+ if not new_version:
+ print("未输入版本号,操作取消")
+ return
+
+ # 更新版本信息
+ if update_version_info(new_version):
+ print("\n版本更新完成!")
+ print("现在可以运行 python build.py 来构建新版本")
+ else:
+ print("\n版本更新失败!")
+
+if __name__ == "__main__":
+ main()
diff --git a/version_info.txt b/version_info.txt
new file mode 100644
index 0000000..508522d
--- /dev/null
+++ b/version_info.txt
@@ -0,0 +1,43 @@
+# UTF-8
+#
+# For more details about fixed file info 'ffi' see:
+# http://msdn.microsoft.com/en-us/library/ms646997.aspx
+VSVersionInfo(
+ ffi=FixedFileInfo(
+ # filevers and prodvers should be always a tuple with four items: (1, 2, 3, 4)
+ # Set not needed items to zero 0.
+ filevers=(1, 4, 0, 0),
+ prodvers=(1, 4, 0, 0),
+ # Contains a bitmask that specifies the valid bits 'flags'r
+ mask=0x3f,
+ # Contains a bitmask that specifies the Boolean attributes of the file.
+ flags=0x0,
+ # The operating system for which this file was designed.
+ # 0x4 - NT and there is no need to change it.
+ OS=0x40004,
+ # The general type of file.
+ # 0x1 - the file is an application.
+ fileType=0x1,
+ # The function of the file.
+ # 0x0 - the function is not defined for this fileType
+ subtype=0x0,
+ # Creation date and time stamp.
+ date=(0, 0)
+ ),
+ kids=[
+ StringFileInfo(
+ [
+ StringTable(
+ u'040904B0',
+ [StringStruct(u'CompanyName', u'HarmonyDevTools'),
+ StringStruct(u'FileDescription', u'HarmonyOS/OpenHarmony开发工具'),
+ StringStruct(u'FileVersion', u'1.4.0'),
+ StringStruct(u'InternalName', u'HarmonyDevTools'),
+ StringStruct(u'LegalCopyright', u'Copyright (C) 2025 DevWiki'),
+ StringStruct(u'OriginalFilename', u'HarmonyDevTools.exe'),
+ StringStruct(u'ProductName', u'HarmonyDevTools'),
+ StringStruct(u'ProductVersion', u'1.4.0')])
+ ]),
+ VarFileInfo([VarStruct(u'Translation', [1033, 1200])])
+ ]
+)