mirror of
https://github.com/Dev-Wiki/HarmonyDevTools.git
synced 2025-08-30 01:14:44 +08:00
gui改为使用python 不再依赖外部的运行时
This commit is contained in:
parent
1f15856a68
commit
2590b47137
112
.gitignore
vendored
112
.gitignore
vendored
@ -1,4 +1,108 @@
|
|||||||
/.idea
|
# Python运行时文件
|
||||||
*/obj
|
__pycache__/
|
||||||
*/bin
|
*.py[cod]
|
||||||
*/pack/output
|
*$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/
|
||||||
|
@ -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
|
|
@ -1,3 +0,0 @@
|
|||||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
|
||||||
<s:Boolean x:Key="/Default/AddReferences/RecentPaths/=D_003A_005CCode_005Cgitea_005CHarmonyDevTools_005Clibs_005Clog4net_002Edll/@EntryIndexedValue">True</s:Boolean>
|
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/AnalysisEnabled/@EntryValue">SOLUTION</s:String></wpf:ResourceDictionary>
|
|
@ -1,9 +0,0 @@
|
|||||||
<Application x:Class="HarmonyDevTools.App"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:local="clr-namespace:HarmonyDevTools"
|
|
||||||
StartupUri="MainWindow.xaml">
|
|
||||||
<Application.Resources>
|
|
||||||
|
|
||||||
</Application.Resources>
|
|
||||||
</Application>
|
|
@ -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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interaction logic for App.xaml
|
|
||||||
/// </summary>
|
|
||||||
public partial class App : Application
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -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)
|
|
||||||
)]
|
|
@ -1,31 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<OutputType>WinExe</OutputType>
|
|
||||||
<TargetFramework>net6.0-windows</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<UseWPF>true</UseWPF>
|
|
||||||
<ApplicationIcon>icon.ico</ApplicationIcon>
|
|
||||||
<Title>HarmonyDevTools</Title>
|
|
||||||
<Authors>DevWiki</Authors>
|
|
||||||
<Description>OpenHarmony and HarmonyOS Develop Tools</Description>
|
|
||||||
<Copyright>DevWiki</Copyright>
|
|
||||||
<PackageIcon>icon.ico</PackageIcon>
|
|
||||||
<PackageTags>OpenHarmony,HarmonyOS</PackageTags>
|
|
||||||
<AssemblyVersion>1.0.2</AssemblyVersion>
|
|
||||||
<FileVersion>1.0.2</FileVersion>
|
|
||||||
<Version>1.0.2</Version>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="log4net">
|
|
||||||
<HintPath>..\libs\log4net.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
|
|
||||||
<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
|
|
||||||
<Exec Command="xcopy "$(ProjectDir)..\toolchains" "$(TargetDir)toolchains" /E /I /Y" />
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
</Project>
|
|
@ -1,76 +0,0 @@
|
|||||||
<Window x:Class="HarmonyDevTools.MainWindow"
|
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
|
||||||
xmlns:control="clr-namespace:HarmonyDevTools.control"
|
|
||||||
mc:Ignorable="d" ResizeMode="NoResize"
|
|
||||||
WindowStartupLocation="CenterScreen"
|
|
||||||
Title="HDC Tools" Height="600" Width="740" Margin="10">
|
|
||||||
<Grid>
|
|
||||||
<Grid.ColumnDefinitions>
|
|
||||||
<ColumnDefinition Width="*"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Grid.RowDefinitions>
|
|
||||||
<RowDefinition Height="48"/>
|
|
||||||
<RowDefinition Height="48"/>
|
|
||||||
<RowDefinition Height="48"/>
|
|
||||||
<RowDefinition Height="48"/>
|
|
||||||
<RowDefinition Height="*"/>
|
|
||||||
</Grid.RowDefinitions>
|
|
||||||
<Button x:Name="ListBtn" Grid.Row="0" Grid.Column="0" Width="100" Height="36" Content="列举设备" Click="ListBtn_OnClick" />
|
|
||||||
|
|
||||||
<TextBox x:Name="TargetTb" Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2" Margin="10,0,10,0" VerticalContentAlignment="Center"
|
|
||||||
VerticalAlignment="Center" Height="36" control:TextBoxHelper.Placeholder="请输入connect key"/>
|
|
||||||
|
|
||||||
<Button x:Name="ConnectBtn" Grid.Row="0" Grid.Column="3" Width="100" Height="36" Content="连接设备" Click="ConnectBtn_OnClick" />
|
|
||||||
|
|
||||||
<Button x:Name="KillBtn" Grid.Row="0" Grid.Column="4" Width="100" Height="36" Content="重启hdc" Click="KillBtn_OnClick" />
|
|
||||||
|
|
||||||
<Button x:Name="CheckServerBtn" Grid.Row="0" Grid.Column="5" Width="100" Height="36" Content="版本信息" Click="CheckServerBtn_OnClick" />
|
|
||||||
|
|
||||||
<Border Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" BorderBrush="Red" BorderThickness="1" Margin="5,0,5,0">
|
|
||||||
<StackPanel Orientation="Horizontal" Margin="5,0,5,0">
|
|
||||||
<Button x:Name="InstallBtn" Width="100" Height="36" Content="安装hap"
|
|
||||||
Click="InstallOnClick" VerticalAlignment="Center" />
|
|
||||||
|
|
||||||
<CheckBox x:Name="ReplaceCb" Content="替换安装" IsChecked="False" VerticalAlignment="Center"
|
|
||||||
Margin="10,0,0,0"/>
|
|
||||||
|
|
||||||
<CheckBox x:Name="DowngradeCb" Content="允许降级" IsChecked="False" VerticalAlignment="Center"
|
|
||||||
Margin="10,0,0,0"/>
|
|
||||||
|
|
||||||
<CheckBox x:Name="DynamicCb" Content="动态授权" IsChecked="False" VerticalAlignment="Center"
|
|
||||||
Margin="10,0,0,0"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<Border Grid.Row="1" Grid.Column="3" Grid.ColumnSpan="3" Margin="5,0,5,0" BorderBrush="Red" BorderThickness="1">
|
|
||||||
<StackPanel Orientation="Horizontal" Margin="5,0,5,0">
|
|
||||||
<TextBox x:Name="PackageNameTb" control:TextBoxHelper.Placeholder="输入包名" VerticalAlignment="Center" VerticalContentAlignment="Center"
|
|
||||||
Width="228" Height="36"/>
|
|
||||||
|
|
||||||
<Button x:Name="UninstallBtn" Width="100" Height="36" Content="卸载应用" Margin="10,0,0,0"
|
|
||||||
Click="UninstallBtn_OnClick" VerticalAlignment="Center" />
|
|
||||||
</StackPanel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<Button x:Name="GetPhoto" Grid.Row="3" Grid.Column="0" Width="100" Height="36" Content="导出照片" Click="GetPhoto_OnClick" />
|
|
||||||
|
|
||||||
<Button x:Name="GetUDID" Grid.Row="3" Grid.Column="1" Width="100" Height="36" Content="UDID" Click="GetUDID_OnClick" />
|
|
||||||
|
|
||||||
<TextBox x:Name="CommandTb" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="5" Margin="10,0,10,0"
|
|
||||||
Height="32" VerticalContentAlignment="Center" control:TextBoxHelper.Placeholder="输入命令不需要以 hdc 开头"/>
|
|
||||||
|
|
||||||
<Button x:Name="CommandBtn" Content="执行" Grid.Row="2" Grid.Column="5" Width="100" Height="36" VerticalAlignment="Center"
|
|
||||||
Click="CommandBtn_OnClick" HorizontalAlignment="Center"/>
|
|
||||||
|
|
||||||
<TextBox Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="6" x:Name="ResultTb" Margin="10" TextWrapping="Wrap" IsReadOnly="True"
|
|
||||||
VerticalScrollBarVisibility="Auto"/>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
|
@ -1,136 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Windows;
|
|
||||||
using HarmonyDevTools.util;
|
|
||||||
using Microsoft.Win32;
|
|
||||||
|
|
||||||
namespace HarmonyDevTools;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Interaction logic for MainWindow.xaml
|
|
||||||
/// </summary>
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 157 KiB |
@ -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
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
BIN
HarmonyDevTools_v1.4.0.zip
Normal file
BIN
HarmonyDevTools_v1.4.0.zip
Normal file
Binary file not shown.
323
build.py
Normal file
323
build.py
Normal file
@ -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()
|
@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"sdk": {
|
|
||||||
"version": "6.0.0",
|
|
||||||
"rollForward": "latestMinor",
|
|
||||||
"allowPrerelease": false
|
|
||||||
}
|
|
||||||
}
|
|
BIN
libs/log4net.dll
BIN
libs/log4net.dll
Binary file not shown.
664
main.py
Normal file
664
main.py
Normal file
@ -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('<Return>', lambda e: self.execute_command())
|
||||||
|
|
||||||
|
# 命令输入框绑定上下箭头键(历史记录)
|
||||||
|
self.command_entry.bind('<Up>', lambda e: self.show_command_history('up'))
|
||||||
|
self.command_entry.bind('<Down>', lambda e: self.show_command_history('down'))
|
||||||
|
|
||||||
|
# 窗口大小改变事件
|
||||||
|
self.root.bind('<Configure>', 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()
|
198
readme.md
198
readme.md
@ -1,14 +1,15 @@
|
|||||||
|
# HarmonyDevTools - Python版本
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
||||||
本项目为HarmonyOS/OpenHarmony的 toolchains 工具提供UI操作, 方便使用.
|
本项目为HarmonyOS/OpenHarmony的 toolchains 工具提供Python Tkinter UI操作界面,方便使用。
|
||||||
|
|
||||||
## 1. 版本说明
|
## 1. 版本说明
|
||||||
|
|
||||||
目前支持 hdc 工具的使用,后续会陆续增加其他工具,
|
这是原C# WPF项目的Python Tkinter版本,保持了相同的功能特性:
|
||||||
|
|
||||||
hdc功能支持:
|
hdc功能支持:
|
||||||
- 枚举设备/连接设备
|
- 枚举设备/连接设备
|
||||||
@ -19,34 +20,183 @@ hdc功能支持:
|
|||||||
- 获取手机的UDID
|
- 获取手机的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:
|
**方式二:直接运行Python文件**
|
||||||
- x64版本:https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.31-windows-x64-installer
|
```bash
|
||||||
- x86版本:https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-6.0.31-windows-x86-installer
|
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
|
|
||||||
|
|
||||||
> ## 环境准备
|
```bash
|
||||||
> hdc 工具获取方式:
|
pip install -r requirements.txt
|
||||||
>
|
```
|
||||||
> 通过OpenHarmony sdk获取,hdc在sdk的toolchains目录下。
|
|
||||||
>
|
|
||||||
> **使用举例:**
|
|
||||||
>
|
|
||||||
> 下面以windows侧使用方式举例:
|
|
||||||
>
|
|
||||||
> 获取windows的sdk,将hdc.exe放到磁盘某个位置即可使用。
|
|
||||||
|
|
||||||
|
## 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`目录包含所需的工具文件。
|
||||||
|
14
requirements.txt
Normal file
14
requirements.txt
Normal file
@ -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打包工具
|
37
run.bat
Normal file
37
run.bat
Normal file
@ -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
|
86
update_version.py
Normal file
86
update_version.py
Normal file
@ -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()
|
43
version_info.txt
Normal file
43
version_info.txt
Normal file
@ -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])])
|
||||||
|
]
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user