mirror of
https://github.com/PumpkinTTL/kiro-automation-toolkit.git
synced 2026-08-28 17:41:56 +08:00
85efecf0be
🚀 项目特性: - Windows Sandbox 环境支持 - 完整的自动化安装流程 - 智能等待和界面检测机制 - 多种登录方式支持 - 丰富的调试和分析工具 📁 项目结构: - sandbox/ - 项目根目录 - sandbox_files/ - 沙盒环境脚本 - start_sandbox.ps1 - 主启动脚本 - sandbox_config.wsb - Sandbox 配置 🛠️ 核心功能: - 自动化 Kiro IDE 安装 - 登录界面自动化 - 窗口和控件分析 - 详细的进度反馈 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
315 lines
11 KiB
Python
315 lines
11 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
窗口控件分析器
|
|
功能:连接指定路径的应用程序,等待5秒后分析并打印所有窗口的控件信息
|
|
作者:Claude Code Assistant
|
|
"""
|
|
|
|
import time
|
|
import sys
|
|
import os
|
|
from datetime import datetime
|
|
from pywinauto import Application
|
|
from pywinauto.findwindows import ElementNotFoundError
|
|
|
|
class WindowAnalyzer:
|
|
def __init__(self):
|
|
self.app = None
|
|
self.analysis_results = []
|
|
|
|
def connect_to_application(self, app_path, timeout=30):
|
|
"""
|
|
连接到指定路径的应用程序
|
|
|
|
Args:
|
|
app_path (str): 应用程序路径或进程名
|
|
timeout (int): 连接超时时间(秒)
|
|
|
|
Returns:
|
|
bool: 连接是否成功
|
|
"""
|
|
print(f"🔗 正在连接应用程序: {app_path}")
|
|
print(f"⏱️ 连接超时时间: {timeout}秒")
|
|
|
|
# 特殊处理kiro.exe - 支持安装程序和已安装的应用程序
|
|
if "kiro.exe" in app_path.lower():
|
|
print("🔍 检测到kiro.exe,尝试连接进程...")
|
|
connection_methods = [
|
|
# 安装程序连接方法(保留原有功能)
|
|
lambda: Application(backend="uia").connect(title_re=".*[Ss]etup.*", timeout=5),
|
|
lambda: Application(backend="uia").connect(title_re=".*[Ii]nstall.*", timeout=5),
|
|
lambda: Application(backend="uia").connect(title_re=".*安装.*", timeout=5),
|
|
lambda: Application(backend="win32").connect(title_re=".*[Ss]etup.*", timeout=5),
|
|
lambda: Application(backend="win32").connect(title_re=".*[Ii]nstall.*", timeout=5),
|
|
lambda: Application(backend="win32").connect(title_re=".*安装.*", timeout=5),
|
|
# 已安装应用程序连接方法(新增功能)
|
|
lambda: Application(backend="uia").connect(title_re=".*Kiro.*", timeout=5),
|
|
lambda: Application(backend="win32").connect(title_re=".*Kiro.*", timeout=5),
|
|
lambda: Application(backend="uia").connect(title_re=".*Getting started.*", timeout=5),
|
|
lambda: Application(backend="win32").connect(title_re=".*Getting started.*", timeout=5),
|
|
lambda: Application(backend="uia").connect(path=app_path, timeout=5),
|
|
lambda: Application(backend="win32").connect(path=app_path, timeout=5),
|
|
]
|
|
else:
|
|
# 原有的连接方式
|
|
connection_methods = [
|
|
lambda: Application(backend="uia").connect(path=app_path, timeout=5),
|
|
lambda: Application(backend="win32").connect(path=app_path, timeout=5),
|
|
lambda: Application(backend="uia").connect(title_re=f".*{os.path.basename(app_path)}.*", timeout=5),
|
|
]
|
|
|
|
start_time = time.time()
|
|
attempt = 0
|
|
|
|
while time.time() - start_time < timeout:
|
|
attempt += 1
|
|
print(f"🔄 连接尝试 {attempt}...")
|
|
|
|
for i, method in enumerate(connection_methods):
|
|
try:
|
|
self.app = method()
|
|
print(f"✅ 连接成功! 使用方法 {i+1}")
|
|
print(f"📋 进程ID: {self.app.process}")
|
|
return True
|
|
except Exception as e:
|
|
print(f" 方法 {i+1} 失败: {str(e)[:50]}...")
|
|
continue
|
|
|
|
time.sleep(1)
|
|
|
|
print(f"❌ 连接失败: 超时 {timeout}秒")
|
|
return False
|
|
|
|
def analyze_control(self, ctrl, level=0):
|
|
"""
|
|
分析单个控件的详细信息
|
|
|
|
Args:
|
|
ctrl: pywinauto控件对象
|
|
level (int): 层级深度,用于缩进显示
|
|
|
|
Returns:
|
|
dict: 控件信息字典
|
|
"""
|
|
indent = " " * level
|
|
control_info = {}
|
|
|
|
try:
|
|
# 基本信息
|
|
control_info['text'] = ctrl.window_text()
|
|
control_info['class_name'] = ctrl.class_name()
|
|
control_info['visible'] = ctrl.is_visible()
|
|
control_info['enabled'] = ctrl.is_enabled()
|
|
|
|
# UIA特有属性
|
|
if hasattr(ctrl, 'element_info'):
|
|
try:
|
|
control_info['control_type'] = ctrl.element_info.control_type
|
|
control_info['automation_id'] = ctrl.element_info.automation_id
|
|
control_info['name'] = ctrl.element_info.name
|
|
except:
|
|
pass
|
|
|
|
# 位置信息
|
|
try:
|
|
rect = ctrl.rectangle()
|
|
control_info['position'] = f"({rect.left},{rect.top})-({rect.right},{rect.bottom})"
|
|
control_info['size'] = f"{rect.width()}x{rect.height()}"
|
|
except:
|
|
control_info['position'] = "未知"
|
|
control_info['size'] = "未知"
|
|
|
|
except Exception as e:
|
|
control_info['error'] = str(e)
|
|
|
|
return control_info
|
|
|
|
def print_control_tree(self, window, max_depth=3):
|
|
"""
|
|
打印窗口的控件树结构
|
|
|
|
Args:
|
|
window: pywinauto窗口对象
|
|
max_depth (int): 最大遍历深度
|
|
"""
|
|
def print_control_recursive(ctrl, level=0):
|
|
if level > max_depth:
|
|
return
|
|
|
|
indent = " " * level
|
|
info = self.analyze_control(ctrl, level)
|
|
|
|
# 格式化输出
|
|
display_text = info.get('text', '').strip()
|
|
if not display_text:
|
|
display_text = info.get('name', '').strip()
|
|
if not display_text:
|
|
display_text = f"[{info.get('class_name', 'Unknown')}]"
|
|
|
|
status_indicators = []
|
|
if not info.get('visible', True):
|
|
status_indicators.append("隐藏")
|
|
if not info.get('enabled', True):
|
|
status_indicators.append("禁用")
|
|
|
|
status_str = f" ({', '.join(status_indicators)})" if status_indicators else ""
|
|
|
|
print(f"{indent}├─ {display_text}{status_str}")
|
|
|
|
# 详细信息
|
|
if info.get('control_type'):
|
|
print(f"{indent}│ 类型: {info['control_type']}")
|
|
if info.get('automation_id'):
|
|
print(f"{indent}│ ID: {info['automation_id']}")
|
|
if info.get('class_name'):
|
|
print(f"{indent}│ 类名: {info['class_name']}")
|
|
if info.get('position') != "未知":
|
|
print(f"{indent}│ 位置: {info['position']} 大小: {info['size']}")
|
|
|
|
# 递归处理子控件
|
|
try:
|
|
children = ctrl.children()
|
|
for child in children[:20]: # 限制子控件数量避免输出过多
|
|
print_control_recursive(child, level + 1)
|
|
|
|
if len(children) > 20:
|
|
print(f"{indent} └─ ... 还有 {len(children) - 20} 个子控件")
|
|
|
|
except Exception as e:
|
|
if level == 0: # 只在顶级显示错误
|
|
print(f"{indent}│ ⚠️ 获取子控件失败: {e}")
|
|
|
|
try:
|
|
print_control_recursive(window)
|
|
except Exception as e:
|
|
print(f"❌ 分析窗口控件失败: {e}")
|
|
|
|
def analyze_all_windows(self):
|
|
"""
|
|
分析应用程序的所有窗口
|
|
"""
|
|
if not self.app:
|
|
print("❌ 应用程序未连接")
|
|
return
|
|
|
|
print("\n" + "="*80)
|
|
print("🔍 开始分析窗口控件结构...")
|
|
print("="*80)
|
|
|
|
try:
|
|
windows = self.app.windows()
|
|
print(f"📊 发现 {len(windows)} 个窗口")
|
|
|
|
for i, window in enumerate(windows, 1):
|
|
try:
|
|
window_title = window.window_text()
|
|
window_class = window.class_name()
|
|
is_visible = window.is_visible()
|
|
is_enabled = window.is_enabled()
|
|
|
|
print(f"\n🪟 窗口 {i}: {window_title or '[无标题]'}")
|
|
print(f" 类名: {window_class}")
|
|
print(f" 状态: {'可见' if is_visible else '隐藏'}, {'启用' if is_enabled else '禁用'}")
|
|
|
|
try:
|
|
rect = window.rectangle()
|
|
print(f" 位置: ({rect.left},{rect.top}) 大小: {rect.width()}x{rect.height()}")
|
|
except:
|
|
print(" 位置: 无法获取")
|
|
|
|
print(f" 控件结构:")
|
|
self.print_control_tree(window)
|
|
|
|
except Exception as e:
|
|
print(f"❌ 分析窗口 {i} 失败: {e}")
|
|
|
|
print("-" * 60)
|
|
|
|
except Exception as e:
|
|
print(f"❌ 获取窗口列表失败: {e}")
|
|
|
|
def save_analysis_to_file(self, filename=None):
|
|
"""
|
|
将分析结果保存到文件
|
|
|
|
Args:
|
|
filename (str): 输出文件名,默认使用时间戳
|
|
"""
|
|
if not filename:
|
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
filename = f"window_analysis_{timestamp}.txt"
|
|
|
|
try:
|
|
# 重定向输出到文件
|
|
import io
|
|
from contextlib import redirect_stdout
|
|
|
|
output_buffer = io.StringIO()
|
|
with redirect_stdout(output_buffer):
|
|
self.analyze_all_windows()
|
|
|
|
with open(filename, 'w', encoding='utf-8') as f:
|
|
f.write(f"窗口控件分析报告\n")
|
|
f.write(f"生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
|
|
f.write("="*80 + "\n\n")
|
|
f.write(output_buffer.getvalue())
|
|
|
|
print(f"📄 分析结果已保存到: {filename}")
|
|
|
|
except Exception as e:
|
|
print(f"❌ 保存文件失败: {e}")
|
|
|
|
def main():
|
|
"""主函数"""
|
|
print("🔧 窗口控件分析器")
|
|
print("="*50)
|
|
|
|
# 获取应用程序路径
|
|
if len(sys.argv) > 1:
|
|
app_path = sys.argv[1]
|
|
print(f"📝 使用命令行参数: {app_path}")
|
|
else:
|
|
app_path = input("请输入应用程序路径或进程名 (例如: kiro.exe 或 C:\\path\\to\\app.exe): ").strip()
|
|
|
|
if not app_path:
|
|
print("❌ 未提供应用程序路径")
|
|
return
|
|
|
|
# 创建分析器
|
|
analyzer = WindowAnalyzer()
|
|
|
|
# 连接应用程序
|
|
if not analyzer.connect_to_application(app_path):
|
|
print("❌ 无法连接到应用程序,请确保:")
|
|
print(" 1. 应用程序正在运行")
|
|
print(" 2. 路径或进程名正确")
|
|
print(" 3. 应用程序有可见的用户界面")
|
|
return
|
|
|
|
# 等待5秒
|
|
print("\n⏳ 等待5秒让应用程序稳定...")
|
|
for i in range(5, 0, -1):
|
|
print(f" {i}秒...", end='\r')
|
|
time.sleep(1)
|
|
print(" 完成! ")
|
|
|
|
# 分析窗口控件
|
|
analyzer.analyze_all_windows()
|
|
|
|
# 询问是否保存到文件
|
|
save_choice = input("\n💾 是否保存分析结果到文件? (y/n): ").lower()
|
|
if save_choice in ['y', 'yes', '是']:
|
|
analyzer.save_analysis_to_file()
|
|
|
|
print("\n✅ 分析完成!")
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except KeyboardInterrupt:
|
|
print("\n\n⚠️ 用户中断操作")
|
|
except Exception as e:
|
|
print(f"\n❌ 程序异常: {e}")
|
|
import traceback
|
|
traceback.print_exc() |