|
@@ -10,11 +10,34 @@ import configparser
|
|
|
from datetime import datetime
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
class MuMuEmulatorManager:
|
|
class MuMuEmulatorManager:
|
|
|
|
|
+ # 类级别的剪贴板锁,所有实例共享
|
|
|
|
|
+ _clipboard_lock = threading.Lock()
|
|
|
|
|
+
|
|
|
def __init__(self, manager_path=r"D:\MuMuPlayer\nx_main\MuMuManager.exe"):
|
|
def __init__(self, manager_path=r"D:\MuMuPlayer\nx_main\MuMuManager.exe"):
|
|
|
self.manager_path = manager_path
|
|
self.manager_path = manager_path
|
|
|
if not os.path.exists(manager_path):
|
|
if not os.path.exists(manager_path):
|
|
|
raise FileNotFoundError(f"找不到 MuMuManager.exe: {manager_path}")
|
|
raise FileNotFoundError(f"找不到 MuMuManager.exe: {manager_path}")
|
|
|
-
|
|
|
|
|
|
|
+ def get_adb_port(self, index, log_callback=None):
|
|
|
|
|
+ """实时获取指定模拟器的 ADB 端口,获取不到就一直等待直到成功"""
|
|
|
|
|
+ while True:
|
|
|
|
|
+ cmd = [self.manager_path, "info", "-v", str(index)]
|
|
|
|
|
+ result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
|
|
|
|
|
+
|
|
|
|
|
+ if result.returncode == 0:
|
|
|
|
|
+ try:
|
|
|
|
|
+ data = json.loads(result.stdout)
|
|
|
|
|
+ adb_port = data.get('adb_port')
|
|
|
|
|
+ if adb_port is not None:
|
|
|
|
|
+ if log_callback:
|
|
|
|
|
+ log_callback(f"✅ 模拟器 {index} ADB端口: {adb_port}")
|
|
|
|
|
+ return adb_port
|
|
|
|
|
+ except:
|
|
|
|
|
+ pass
|
|
|
|
|
+
|
|
|
|
|
+ if log_callback:
|
|
|
|
|
+ log_callback(f"⏳ 模拟器 {index} 等待ADB端口...")
|
|
|
|
|
+
|
|
|
|
|
+ time.sleep(3) # 等待3秒后重试
|
|
|
def get_emulator_list(self):
|
|
def get_emulator_list(self):
|
|
|
"""获取所有模拟器列表"""
|
|
"""获取所有模拟器列表"""
|
|
|
cmd = [self.manager_path, "info", "-v", "all"]
|
|
cmd = [self.manager_path, "info", "-v", "all"]
|
|
@@ -29,8 +52,6 @@ class MuMuEmulatorManager:
|
|
|
for key, value in data.items():
|
|
for key, value in data.items():
|
|
|
if isinstance(value, dict):
|
|
if isinstance(value, dict):
|
|
|
value['index'] = key
|
|
value['index'] = key
|
|
|
- # 计算ADB端口
|
|
|
|
|
- value['adb_port'] = 16384 + 32 * int(key)
|
|
|
|
|
emulators.append(value)
|
|
emulators.append(value)
|
|
|
return emulators
|
|
return emulators
|
|
|
except json.JSONDecodeError:
|
|
except json.JSONDecodeError:
|
|
@@ -77,7 +98,7 @@ class MuMuEmulatorManager:
|
|
|
log_callback(f"❌ APK文件不存在: {apk_path}")
|
|
log_callback(f"❌ APK文件不存在: {apk_path}")
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
- adb_port = 16384 + 32 * int(index)
|
|
|
|
|
|
|
+ adb_port = self.get_adb_port(index)
|
|
|
target_device = f"127.0.0.1:{adb_port}"
|
|
target_device = f"127.0.0.1:{adb_port}"
|
|
|
|
|
|
|
|
# 连接ADB
|
|
# 连接ADB
|
|
@@ -95,7 +116,7 @@ class MuMuEmulatorManager:
|
|
|
|
|
|
|
|
# 安装APK
|
|
# 安装APK
|
|
|
if log_callback:
|
|
if log_callback:
|
|
|
- log_callback(f"正在安装APK: {os.path.basename(apk_path)}")
|
|
|
|
|
|
|
+ log_callback(f"正在安装APK: {os.path.basename(apk_path)} 端口: {adb_port}...")
|
|
|
install_cmd = f"adb -s {target_device} install -r \"{apk_path}\""
|
|
install_cmd = f"adb -s {target_device} install -r \"{apk_path}\""
|
|
|
result = subprocess.run(install_cmd, shell=True, capture_output=True, text=True)
|
|
result = subprocess.run(install_cmd, shell=True, capture_output=True, text=True)
|
|
|
|
|
|
|
@@ -103,7 +124,7 @@ class MuMuEmulatorManager:
|
|
|
|
|
|
|
|
def open_app(self, index, package_name, log_callback=None):
|
|
def open_app(self, index, package_name, log_callback=None):
|
|
|
"""打开应用"""
|
|
"""打开应用"""
|
|
|
- adb_port = 16384 + 32 * int(index)
|
|
|
|
|
|
|
+ adb_port = self.get_adb_port(index)
|
|
|
target_device = f"127.0.0.1:{adb_port}"
|
|
target_device = f"127.0.0.1:{adb_port}"
|
|
|
|
|
|
|
|
subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
|
|
subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
|
|
@@ -118,37 +139,31 @@ class MuMuEmulatorManager:
|
|
|
return True
|
|
return True
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
|
|
+ # 替代方案:直接发送文本字符,不用剪贴板
|
|
|
def paste_text(self, index, text, log_callback=None):
|
|
def paste_text(self, index, text, log_callback=None):
|
|
|
- """粘贴文字"""
|
|
|
|
|
- adb_port = 16384 + 32 * int(index)
|
|
|
|
|
|
|
+ adb_port = self.get_adb_port(index)
|
|
|
target_device = f"127.0.0.1:{adb_port}"
|
|
target_device = f"127.0.0.1:{adb_port}"
|
|
|
|
|
|
|
|
subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
|
|
subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
|
|
|
- time.sleep(0.5)
|
|
|
|
|
-
|
|
|
|
|
- # 复制到剪贴板
|
|
|
|
|
- root = tk.Tk()
|
|
|
|
|
- root.withdraw()
|
|
|
|
|
- root.clipboard_clear()
|
|
|
|
|
- root.clipboard_append(text)
|
|
|
|
|
- root.update()
|
|
|
|
|
- root.destroy()
|
|
|
|
|
-
|
|
|
|
|
- escape_text = text.replace('"', '\\"').replace("'", "\\'")
|
|
|
|
|
- set_clipboard_cmd = f'adb -s {target_device} shell am broadcast -a clipper.set -e text "{escape_text}"'
|
|
|
|
|
- subprocess.run(set_clipboard_cmd, shell=True, capture_output=True)
|
|
|
|
|
time.sleep(0.3)
|
|
time.sleep(0.3)
|
|
|
|
|
|
|
|
- # 执行粘贴
|
|
|
|
|
- subprocess.run(f"adb -s {target_device} shell input keyevent 279", shell=True)
|
|
|
|
|
|
|
+ # 直接通过 ADB 输入文本(逐字符)
|
|
|
|
|
+ # 先确保输入框获得焦点(点击一下)
|
|
|
|
|
+ subprocess.run(f"adb -s {target_device} shell input tap 390 90", shell=True)
|
|
|
|
|
+ time.sleep(0.5)
|
|
|
|
|
+
|
|
|
|
|
+ # 使用 adb shell input text 输入(会自动处理空格和特殊字符)
|
|
|
|
|
+ # 注意:需要用 %s 转义空格
|
|
|
|
|
+ safe_text = text.replace(' ', '%s').replace('&', '\\&')
|
|
|
|
|
+ subprocess.run(f"adb -s {target_device} shell input text '{safe_text}'", shell=True)
|
|
|
|
|
|
|
|
if log_callback:
|
|
if log_callback:
|
|
|
- log_callback(f"✅ 已粘贴: {text[:50]}{'...' if len(text) > 50 else ''}")
|
|
|
|
|
|
|
+ log_callback(f"✅ 已输入: {text[:50]}{'...' if len(text) > 50 else ''}")
|
|
|
return True
|
|
return True
|
|
|
|
|
|
|
|
def tap(self, index, x, y, log_callback=None):
|
|
def tap(self, index, x, y, log_callback=None):
|
|
|
"""点击坐标"""
|
|
"""点击坐标"""
|
|
|
- adb_port = 16384 + 32 * int(index)
|
|
|
|
|
|
|
+ adb_port = self.get_adb_port(index)
|
|
|
target_device = f"127.0.0.1:{adb_port}"
|
|
target_device = f"127.0.0.1:{adb_port}"
|
|
|
|
|
|
|
|
subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
|
|
subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
|
|
@@ -163,31 +178,31 @@ class MuMuEmulatorManager:
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
def get_pixel_color(self, index, x, y, log_callback=None):
|
|
def get_pixel_color(self, index, x, y, log_callback=None):
|
|
|
- """获取模拟器内指定坐标点的颜色
|
|
|
|
|
-
|
|
|
|
|
- Args:
|
|
|
|
|
- index: 模拟器索引
|
|
|
|
|
- x: X坐标
|
|
|
|
|
- y: Y坐标
|
|
|
|
|
- log_callback: 日志回调函数
|
|
|
|
|
-
|
|
|
|
|
- Returns:
|
|
|
|
|
- 颜色值字符串,格式如 "#FFFFFF",失败返回 None
|
|
|
|
|
- """
|
|
|
|
|
- adb_port = 16384 + 32 * int(index)
|
|
|
|
|
|
|
+ """获取模拟器内指定坐标点的颜色"""
|
|
|
|
|
+ adb_port = self.get_adb_port(index)
|
|
|
target_device = f"127.0.0.1:{adb_port}"
|
|
target_device = f"127.0.0.1:{adb_port}"
|
|
|
|
|
|
|
|
- # 连接ADB
|
|
|
|
|
|
|
+ # 连接ADB并验证连接
|
|
|
subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
|
|
subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
|
|
|
time.sleep(0.5)
|
|
time.sleep(0.5)
|
|
|
|
|
|
|
|
|
|
+ # 验证设备是否在线
|
|
|
|
|
+ verify_cmd = f"adb -s {target_device} shell echo 1"
|
|
|
|
|
+ verify_result = subprocess.run(verify_cmd, shell=True, capture_output=True, text=True)
|
|
|
|
|
+ if verify_result.returncode != 0:
|
|
|
|
|
+ if log_callback:
|
|
|
|
|
+ log_callback(f"⚠️ 模拟器 {index} ADB 连接失败,重新连接...")
|
|
|
|
|
+ subprocess.run(f"adb disconnect {target_device}", shell=True, capture_output=True)
|
|
|
|
|
+ time.sleep(1)
|
|
|
|
|
+ subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
|
|
|
|
|
+ time.sleep(1)
|
|
|
|
|
+
|
|
|
try:
|
|
try:
|
|
|
# 获取屏幕分辨率
|
|
# 获取屏幕分辨率
|
|
|
get_resolution_cmd = f"adb -s {target_device} shell wm size"
|
|
get_resolution_cmd = f"adb -s {target_device} shell wm size"
|
|
|
resolution_result = subprocess.run(get_resolution_cmd, shell=True, capture_output=True, text=True)
|
|
resolution_result = subprocess.run(get_resolution_cmd, shell=True, capture_output=True, text=True)
|
|
|
|
|
|
|
|
if resolution_result.stdout:
|
|
if resolution_result.stdout:
|
|
|
- # 解析分辨率,格式如 "Physical size: 720x1280"
|
|
|
|
|
import re
|
|
import re
|
|
|
match = re.search(r'(\d+)x(\d+)', resolution_result.stdout)
|
|
match = re.search(r'(\d+)x(\d+)', resolution_result.stdout)
|
|
|
if match:
|
|
if match:
|
|
@@ -203,9 +218,10 @@ class MuMuEmulatorManager:
|
|
|
if log_callback:
|
|
if log_callback:
|
|
|
log_callback(f"📱 屏幕分辨率: {screen_width}x{screen_height}")
|
|
log_callback(f"📱 屏幕分辨率: {screen_width}x{screen_height}")
|
|
|
|
|
|
|
|
- # 使用 screencap -p 获取 PNG 格式,然后通过 Python 解析
|
|
|
|
|
- # 创建本地临时文件
|
|
|
|
|
- temp_local_file = f"temp_screenshot_{int(time.time())}.png"
|
|
|
|
|
|
|
+ # 使用唯一的临时文件名(包含线程ID和时间戳)
|
|
|
|
|
+ import threading
|
|
|
|
|
+ thread_id = threading.current_thread().ident
|
|
|
|
|
+ temp_local_file = f"temp_screenshot_{thread_id}_{int(time.time()*1000)}.png"
|
|
|
|
|
|
|
|
# 截图并保存到本地
|
|
# 截图并保存到本地
|
|
|
screenshot_cmd = f"adb -s {target_device} exec-out screencap -p > {temp_local_file}"
|
|
screenshot_cmd = f"adb -s {target_device} exec-out screencap -p > {temp_local_file}"
|
|
@@ -242,8 +258,10 @@ class MuMuEmulatorManager:
|
|
|
|
|
|
|
|
color = f"#{r:02X}{g:02X}{b:02X}"
|
|
color = f"#{r:02X}{g:02X}{b:02X}"
|
|
|
|
|
|
|
|
- # 清理临时文件
|
|
|
|
|
- os.remove(temp_local_file)
|
|
|
|
|
|
|
+ # 关闭图片并清理临时文件
|
|
|
|
|
+ img.close()
|
|
|
|
|
+ if os.path.exists(temp_local_file):
|
|
|
|
|
+ os.remove(temp_local_file)
|
|
|
|
|
|
|
|
if log_callback:
|
|
if log_callback:
|
|
|
log_callback(f"🎨 坐标({x},{y}) 颜色: {color}")
|
|
log_callback(f"🎨 坐标({x},{y}) 颜色: {color}")
|
|
@@ -252,22 +270,28 @@ class MuMuEmulatorManager:
|
|
|
except ImportError:
|
|
except ImportError:
|
|
|
if log_callback:
|
|
if log_callback:
|
|
|
log_callback("❌ 请先安装PIL库: pip install Pillow")
|
|
log_callback("❌ 请先安装PIL库: pip install Pillow")
|
|
|
- # 清理临时文件
|
|
|
|
|
if os.path.exists(temp_local_file):
|
|
if os.path.exists(temp_local_file):
|
|
|
os.remove(temp_local_file)
|
|
os.remove(temp_local_file)
|
|
|
return None
|
|
return None
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
if log_callback:
|
|
if log_callback:
|
|
|
log_callback(f"❌ 解析图片失败: {e}")
|
|
log_callback(f"❌ 解析图片失败: {e}")
|
|
|
- # 清理临时文件
|
|
|
|
|
if os.path.exists(temp_local_file):
|
|
if os.path.exists(temp_local_file):
|
|
|
- os.remove(temp_local_file)
|
|
|
|
|
|
|
+ try:
|
|
|
|
|
+ os.remove(temp_local_file)
|
|
|
|
|
+ except:
|
|
|
|
|
+ pass
|
|
|
return None
|
|
return None
|
|
|
else:
|
|
else:
|
|
|
if log_callback:
|
|
if log_callback:
|
|
|
log_callback("❌ 截图失败")
|
|
log_callback("❌ 截图失败")
|
|
|
|
|
+ if os.path.exists(temp_local_file):
|
|
|
|
|
+ try:
|
|
|
|
|
+ os.remove(temp_local_file)
|
|
|
|
|
+ except:
|
|
|
|
|
+ pass
|
|
|
return None
|
|
return None
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
if log_callback:
|
|
if log_callback:
|
|
|
log_callback(f"❌ 获取颜色失败: {e}")
|
|
log_callback(f"❌ 获取颜色失败: {e}")
|
|
@@ -292,7 +316,9 @@ class MuMuAutoGUI:
|
|
|
self.current_thread = None
|
|
self.current_thread = None
|
|
|
self.selected_emulators = []
|
|
self.selected_emulators = []
|
|
|
|
|
|
|
|
-
|
|
|
|
|
|
|
+ self.selected_emulators = []
|
|
|
|
|
+ self.thread_semaphore = None # 添加信号量控制并发数
|
|
|
|
|
+
|
|
|
self.load_btn = None
|
|
self.load_btn = None
|
|
|
self.start_read_btn = None
|
|
self.start_read_btn = None
|
|
|
self.start_comment_btn = None
|
|
self.start_comment_btn = None
|
|
@@ -448,6 +474,10 @@ class MuMuAutoGUI:
|
|
|
self.load_btn = ttk.Button(button_frame, text="读取模拟器", command=self.load_emulators)
|
|
self.load_btn = ttk.Button(button_frame, text="读取模拟器", command=self.load_emulators)
|
|
|
self.load_btn.pack(side='left', padx=5)
|
|
self.load_btn.pack(side='left', padx=5)
|
|
|
|
|
|
|
|
|
|
+ # 添加全选按钮
|
|
|
|
|
+ self.select_all_btn = ttk.Button(button_frame, text="全选", command=self.select_all_emulators, width=6)
|
|
|
|
|
+ self.select_all_btn.pack(side='left', padx=5)
|
|
|
|
|
+
|
|
|
self.start_read_btn = ttk.Button(button_frame, text="开始阅读", command=self.start_task, width=10)
|
|
self.start_read_btn = ttk.Button(button_frame, text="开始阅读", command=self.start_task, width=10)
|
|
|
self.start_read_btn.pack(side='left', padx=10)
|
|
self.start_read_btn.pack(side='left', padx=10)
|
|
|
|
|
|
|
@@ -465,21 +495,19 @@ class MuMuAutoGUI:
|
|
|
tree_frame.pack(fill='both', expand=True, padx=5, pady=5)
|
|
tree_frame.pack(fill='both', expand=True, padx=5, pady=5)
|
|
|
|
|
|
|
|
# 创建Treeview
|
|
# 创建Treeview
|
|
|
- columns = ("选择", "索引", "名称", "ADB端口", "状态")
|
|
|
|
|
|
|
+ columns = ("选择", "索引", "名称", "状态")
|
|
|
self.emulator_tree = ttk.Treeview(tree_frame, columns=columns, show='headings', height=8)
|
|
self.emulator_tree = ttk.Treeview(tree_frame, columns=columns, show='headings', height=8)
|
|
|
|
|
|
|
|
# 设置列标题
|
|
# 设置列标题
|
|
|
self.emulator_tree.heading("选择", text="选择")
|
|
self.emulator_tree.heading("选择", text="选择")
|
|
|
self.emulator_tree.heading("索引", text="索引")
|
|
self.emulator_tree.heading("索引", text="索引")
|
|
|
self.emulator_tree.heading("名称", text="名称")
|
|
self.emulator_tree.heading("名称", text="名称")
|
|
|
- self.emulator_tree.heading("ADB端口", text="ADB端口")
|
|
|
|
|
self.emulator_tree.heading("状态", text="状态")
|
|
self.emulator_tree.heading("状态", text="状态")
|
|
|
|
|
|
|
|
# 设置列宽
|
|
# 设置列宽
|
|
|
self.emulator_tree.column("选择", width=50)
|
|
self.emulator_tree.column("选择", width=50)
|
|
|
self.emulator_tree.column("索引", width=50)
|
|
self.emulator_tree.column("索引", width=50)
|
|
|
self.emulator_tree.column("名称", width=150)
|
|
self.emulator_tree.column("名称", width=150)
|
|
|
- self.emulator_tree.column("ADB端口", width=80)
|
|
|
|
|
self.emulator_tree.column("状态", width=100)
|
|
self.emulator_tree.column("状态", width=100)
|
|
|
|
|
|
|
|
# 添加滚动条
|
|
# 添加滚动条
|
|
@@ -519,6 +547,14 @@ class MuMuAutoGUI:
|
|
|
if path:
|
|
if path:
|
|
|
self.apk_path_var.set(path)
|
|
self.apk_path_var.set(path)
|
|
|
|
|
|
|
|
|
|
+ def select_all_emulators(self):
|
|
|
|
|
+ """全选所有模拟器"""
|
|
|
|
|
+ for item in self.emulator_tree.get_children():
|
|
|
|
|
+ values = self.emulator_tree.item(item, 'values')
|
|
|
|
|
+ if values[0] == "□":
|
|
|
|
|
+ self.emulator_tree.item(item, values=("☑", values[1], values[2], values[3], values[4]))
|
|
|
|
|
+ self.log_message("已全选所有模拟器")
|
|
|
|
|
+
|
|
|
def load_emulators(self):
|
|
def load_emulators(self):
|
|
|
"""加载模拟器列表"""
|
|
"""加载模拟器列表"""
|
|
|
try:
|
|
try:
|
|
@@ -532,7 +568,7 @@ class MuMuAutoGUI:
|
|
|
# 添加模拟器
|
|
# 添加模拟器
|
|
|
for emu in emulators:
|
|
for emu in emulators:
|
|
|
status = "运行中" if emu.get('is_process_started') else "未运行"
|
|
status = "运行中" if emu.get('is_process_started') else "未运行"
|
|
|
- self.emulator_tree.insert('', 'end', values=("□", emu.get('index'), emu.get('name'), emu.get('adb_port'), status))
|
|
|
|
|
|
|
+ self.emulator_tree.insert('', 'end', values=("□", emu.get('index'), emu.get('name'), status))
|
|
|
|
|
|
|
|
self.log_message(f"已加载 {len(emulators)} 个模拟器")
|
|
self.log_message(f"已加载 {len(emulators)} 个模拟器")
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
@@ -549,7 +585,7 @@ class MuMuAutoGUI:
|
|
|
values = self.emulator_tree.item(item, 'values')
|
|
values = self.emulator_tree.item(item, 'values')
|
|
|
current = values[0]
|
|
current = values[0]
|
|
|
new_value = "☑" if current == "□" else "□"
|
|
new_value = "☑" if current == "□" else "□"
|
|
|
- self.emulator_tree.item(item, values=(new_value, values[1], values[2], values[3], values[4]))
|
|
|
|
|
|
|
+ self.emulator_tree.item(item, values=(new_value, values[1], values[2], values[3]))
|
|
|
|
|
|
|
|
def get_selected_emulators(self):
|
|
def get_selected_emulators(self):
|
|
|
"""获取选中的模拟器"""
|
|
"""获取选中的模拟器"""
|
|
@@ -559,8 +595,7 @@ class MuMuAutoGUI:
|
|
|
if values[0] == "☑":
|
|
if values[0] == "☑":
|
|
|
selected.append({
|
|
selected.append({
|
|
|
'index': values[1],
|
|
'index': values[1],
|
|
|
- 'name': values[2],
|
|
|
|
|
- 'adb_port': values[3]
|
|
|
|
|
|
|
+ 'name': values[2]
|
|
|
})
|
|
})
|
|
|
return selected
|
|
return selected
|
|
|
|
|
|
|
@@ -636,104 +671,177 @@ class MuMuAutoGUI:
|
|
|
self.is_paused = False
|
|
self.is_paused = False
|
|
|
self.log_message("⏹ 正在停止任务...")
|
|
self.log_message("⏹ 正在停止任务...")
|
|
|
|
|
|
|
|
|
|
+ def openBook(self, manager, index):
|
|
|
|
|
+ # 执行操作
|
|
|
|
|
+ manager.tap(index, 390, 90, self.log_message)
|
|
|
|
|
+ while manager.get_pixel_color(index, 560, 65, log_callback=self.log_message) != "#F7F7F7":
|
|
|
|
|
+ self.log_message(f"模拟器 {index} 等待搜索页面准备就绪...")
|
|
|
|
|
+ time.sleep(3)
|
|
|
|
|
+ time.sleep(3)
|
|
|
|
|
+ # 点输入框
|
|
|
|
|
+ manager.tap(index, 340, 90, self.log_message)
|
|
|
|
|
+ time.sleep(3)
|
|
|
|
|
+ # 黏贴
|
|
|
|
|
+ manager.paste_text(index, self.search_content_var.get(), self.log_message)
|
|
|
|
|
+ time.sleep(3)
|
|
|
|
|
+ while manager.get_pixel_color(index, 435, 880, log_callback=self.log_message) != "#FFFFFF":
|
|
|
|
|
+ self.log_message(f"模拟器 {index} 没有输入搜索内容...")
|
|
|
|
|
+ # 点输入框
|
|
|
|
|
+ manager.tap(index, 556, 87, self.log_message)
|
|
|
|
|
+ time.sleep(1)
|
|
|
|
|
+ manager.tap(index, 340, 90, self.log_message)
|
|
|
|
|
+ time.sleep(2)
|
|
|
|
|
+ # 黏贴
|
|
|
|
|
+ manager.paste_text(index, self.search_content_var.get(), self.log_message)
|
|
|
|
|
+ time.sleep(3)
|
|
|
|
|
+ manager.tap(index, 655, 92, self.log_message)
|
|
|
|
|
+ time.sleep(6)
|
|
|
|
|
+ while manager.get_pixel_color(index, 630, 235, log_callback=self.log_message) != "#FFFFFF":
|
|
|
|
|
+ self.log_message(f"模拟器 {index} 等待搜索结果...")
|
|
|
|
|
+ time.sleep(3)
|
|
|
|
|
+
|
|
|
|
|
+ # 进入书目
|
|
|
|
|
+ time.sleep(6)
|
|
|
|
|
+ manager.tap(index, 355, 333, self.log_message)
|
|
|
|
|
+ time.sleep(5)
|
|
|
|
|
+ while manager.get_pixel_color(index, 630, 235, log_callback=self.log_message) == "#FFFFFF":
|
|
|
|
|
+ self.log_message(f"模拟器 {index} 还在搜索结果页面,重新点击...")
|
|
|
|
|
+ manager.tap(index, 355, 333, self.log_message)
|
|
|
|
|
+ time.sleep(5)
|
|
|
|
|
+
|
|
|
def run_task(self):
|
|
def run_task(self):
|
|
|
- """执行任务"""
|
|
|
|
|
|
|
+ """执行任务(支持并发)"""
|
|
|
try:
|
|
try:
|
|
|
manager = MuMuEmulatorManager(self.mumu_path_var.get())
|
|
manager = MuMuEmulatorManager(self.mumu_path_var.get())
|
|
|
|
|
|
|
|
- for i, emu in enumerate(self.selected_emulators):
|
|
|
|
|
|
|
+ # 获取最大线程数
|
|
|
|
|
+ max_threads = int(self.max_threads_var.get()) if self.max_threads_var.get().isdigit() else 1
|
|
|
|
|
+ self.thread_semaphore = threading.Semaphore(max_threads)
|
|
|
|
|
+ self.log_message(f"📌 最大并发数: {max_threads}")
|
|
|
|
|
+
|
|
|
|
|
+ # 用于存储线程的列表
|
|
|
|
|
+ threads = []
|
|
|
|
|
+ results = {} # 存储每个模拟器的执行结果
|
|
|
|
|
+
|
|
|
|
|
+ def process_emulator(emu):
|
|
|
|
|
+ """处理单个模拟器的函数"""
|
|
|
|
|
+ with self.thread_semaphore:
|
|
|
|
|
+ if self.should_stop:
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ index = emu['index']
|
|
|
|
|
+ self.log_message(f"\n{'='*50}")
|
|
|
|
|
+ self.log_message(f"开始处理模拟器 {index} ({emu['name']})")
|
|
|
|
|
+ self.log_message(f"{'='*50}")
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ # 启动模拟器
|
|
|
|
|
+ self.log_message(f"正在启动模拟器 {index}...")
|
|
|
|
|
+ if not manager.start_emulator(index):
|
|
|
|
|
+ self.log_message(f"❌ 模拟器 {index} 启动失败")
|
|
|
|
|
+ results[index] = False
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ # 等待就绪
|
|
|
|
|
+ if not manager.wait_for_emulator_ready(index, timeout=180, log_callback=self.log_message):
|
|
|
|
|
+ self.log_message(f"❌ 模拟器 {index} 启动超时")
|
|
|
|
|
+ results[index] = False
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ time.sleep(5)
|
|
|
|
|
+
|
|
|
|
|
+ # 安装APK
|
|
|
|
|
+ if not manager.install_apk(index, self.apk_path_var.get(), self.log_message):
|
|
|
|
|
+ self.log_message(f"❌ APK安装失败")
|
|
|
|
|
+ # results[index] = False
|
|
|
|
|
+ # return
|
|
|
|
|
+
|
|
|
|
|
+ # 打开应用
|
|
|
|
|
+ manager.open_app(index, self.package_name_var.get(), self.log_message)
|
|
|
|
|
+ # 判断是否需要同意
|
|
|
|
|
+ time.sleep(20)
|
|
|
|
|
+ button_color = manager.get_pixel_color(index, 394, 846, log_callback=self.log_message)
|
|
|
|
|
+ self.log_message(button_color)
|
|
|
|
|
+ # 是否是第一次进入
|
|
|
|
|
+ if (button_color and button_color.upper() == "#FC7838"):
|
|
|
|
|
+ manager.tap(index, 394, 846, self.log_message)
|
|
|
|
|
+ time.sleep(90)
|
|
|
|
|
+ manager.tap(index, 640, 260, self.log_message)
|
|
|
|
|
+ time.sleep(10)
|
|
|
|
|
+ manager.tap(index, 57, 193, self.log_message)
|
|
|
|
|
+ time.sleep(5)
|
|
|
|
|
+ else:
|
|
|
|
|
+ time.sleep(10)
|
|
|
|
|
+
|
|
|
|
|
+ while manager.get_pixel_color(index, 560, 130, log_callback=self.log_message) != "#EEF8EE":
|
|
|
|
|
+ self.log_message(f"模拟器 {index} 等待进入主界面中...")
|
|
|
|
|
+ manager.get_pixel_color(index, 640, 260, log_callback=self.log_message)
|
|
|
|
|
+ time.sleep(5)
|
|
|
|
|
+ # 执行操作
|
|
|
|
|
+ self.openBook(manager, index)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ time.sleep(8)
|
|
|
|
|
+ # 翻页
|
|
|
|
|
+ for page in range(int(self.page_count_var.get()) if self.page_count_var.get().isdigit() else 5):
|
|
|
|
|
+ # 判断是否在看书目录界面
|
|
|
|
|
+ while manager.get_pixel_color(index, 712, 147, log_callback=self.log_message) not in ["#E8E3CE", "#E0DBC6"]:
|
|
|
|
|
+ self.log_message(f"模拟器 {index} 不在看书目录界面,等待中...")
|
|
|
|
|
+ # 判断是否有广告
|
|
|
|
|
+ time.sleep(2)
|
|
|
|
|
+ if manager.get_pixel_color(index, 700, 1200, log_callback=self.log_message) in ["#e8e3ce"]:
|
|
|
|
|
+ self.log_message(f"模拟器 {index} 点击跳过广告!")
|
|
|
|
|
+ manager.tap(index, 700, 1200, self.log_message)
|
|
|
|
|
+ time.sleep(3)
|
|
|
|
|
+ if self.should_stop or self.is_paused:
|
|
|
|
|
+ break
|
|
|
|
|
+ intervalNum = 30
|
|
|
|
|
+ if ('-' in self.page_interval_var.get()):
|
|
|
|
|
+ parts = self.page_interval_var.get().split('-')
|
|
|
|
|
+ if len(parts) == 2 and parts[0].isdigit() and parts[1].isdigit():
|
|
|
|
|
+ min_interval = int(parts[0])
|
|
|
|
|
+ max_interval = int(parts[1])
|
|
|
|
|
+ intervalNum = random.randint(min_interval, max_interval)
|
|
|
|
|
+ else:
|
|
|
|
|
+ intervalNum = int(self.page_interval_var.get()) if self.page_interval_var.get().isdigit() else 30
|
|
|
|
|
+ time.sleep(intervalNum)
|
|
|
|
|
+ # manager.tap(index, 700, 1055, self.log_message)
|
|
|
|
|
+ manager.tap(index, 700, 1055, self.log_message)
|
|
|
|
|
+ time.sleep(2)
|
|
|
|
|
+ # 加入书签
|
|
|
|
|
+ self.log_message(f"正在加入书架 {index}...")
|
|
|
|
|
+ manager.tap(index, 360, 600, self.log_message)
|
|
|
|
|
+ time.sleep(2)
|
|
|
|
|
+ manager.tap(index, 255, 97, self.log_message)
|
|
|
|
|
+ time.sleep(3)
|
|
|
|
|
+ # 关闭模拟器
|
|
|
|
|
+ self.log_message(f"正在关闭模拟器 {index}...")
|
|
|
|
|
+ manager.stop_emulator(index)
|
|
|
|
|
+
|
|
|
|
|
+ self.log_message(f"✅ 模拟器 {index} 任务完成")
|
|
|
|
|
+ results[index] = True
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ self.log_message(f"❌ 模拟器 {index} 执行出错: {e}")
|
|
|
|
|
+ results[index] = False
|
|
|
|
|
+
|
|
|
|
|
+ # 启动所有模拟器任务(信号量会自动控制并发数)
|
|
|
|
|
+ for emu in self.selected_emulators:
|
|
|
if self.should_stop:
|
|
if self.should_stop:
|
|
|
- self.log_message("任务已停止")
|
|
|
|
|
break
|
|
break
|
|
|
-
|
|
|
|
|
- while self.is_paused and not self.should_stop:
|
|
|
|
|
- time.sleep(1)
|
|
|
|
|
-
|
|
|
|
|
- index = emu['index']
|
|
|
|
|
- self.log_message(f"\n{'='*50}")
|
|
|
|
|
- self.log_message(f"开始处理模拟器 {index} ({emu['name']})")
|
|
|
|
|
- self.log_message(f"{'='*50}")
|
|
|
|
|
-
|
|
|
|
|
- # 启动模拟器
|
|
|
|
|
- self.log_message(f"正在启动模拟器 {index}...")
|
|
|
|
|
- if not manager.start_emulator(index):
|
|
|
|
|
- self.log_message(f"❌ 模拟器 {index} 启动失败")
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- # 等待就绪
|
|
|
|
|
- if not manager.wait_for_emulator_ready(index, timeout=180, log_callback=self.log_message):
|
|
|
|
|
- self.log_message(f"❌ 模拟器 {index} 启动超时")
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- time.sleep(5)
|
|
|
|
|
-
|
|
|
|
|
- # 安装APK
|
|
|
|
|
- if not manager.install_apk(index, self.apk_path_var.get(), self.log_message):
|
|
|
|
|
- self.log_message(f"❌ APK安装失败")
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- # 打开应用
|
|
|
|
|
- manager.open_app(index, self.package_name_var.get(), self.log_message)
|
|
|
|
|
- # 判断是否需要同意
|
|
|
|
|
|
|
+ # 等待直到有空闲槽位(信号量内部处理)
|
|
|
|
|
+ thread = threading.Thread(target=process_emulator, args=(emu,))
|
|
|
|
|
+ thread.start()
|
|
|
|
|
+ threads.append(thread)
|
|
|
|
|
+ # 稍微延迟一下,避免同时启动太多
|
|
|
time.sleep(10)
|
|
time.sleep(10)
|
|
|
- button_color = manager.get_pixel_color(index, 394, 846, log_callback=self.log_message)
|
|
|
|
|
- self.log_message(button_color)
|
|
|
|
|
- if (button_color and button_color.upper() == "#FC7838"):
|
|
|
|
|
- manager.tap(index, 394, 846, self.log_message)
|
|
|
|
|
- time.sleep(70)
|
|
|
|
|
- manager.tap(index, 640, 260, self.log_message)
|
|
|
|
|
- time.sleep(5)
|
|
|
|
|
- manager.tap(index, 57, 193, self.log_message)
|
|
|
|
|
- time.sleep(5)
|
|
|
|
|
- else:
|
|
|
|
|
- time.sleep(30)
|
|
|
|
|
-
|
|
|
|
|
- # 执行操作
|
|
|
|
|
- manager.tap(index, 390, 90, self.log_message)
|
|
|
|
|
- time.sleep(2)
|
|
|
|
|
- manager.paste_text(index, self.search_content_var.get(), self.log_message)
|
|
|
|
|
- time.sleep(2)
|
|
|
|
|
- manager.tap(index, 655, 92, self.log_message)
|
|
|
|
|
-
|
|
|
|
|
- # 进入书目
|
|
|
|
|
- time.sleep(5)
|
|
|
|
|
- manager.tap(index, 355, 333, self.log_message)
|
|
|
|
|
-
|
|
|
|
|
- time.sleep(8)
|
|
|
|
|
- # 翻页10次
|
|
|
|
|
- for page in range(int(self.page_count_var.get()) if self.page_count_var.get().isdigit() else 5):
|
|
|
|
|
- if self.should_stop or self.is_paused:
|
|
|
|
|
- break
|
|
|
|
|
- intervalNum = 30
|
|
|
|
|
- if ('-' in self.page_interval_var.get()):
|
|
|
|
|
- parts = self.page_interval_var.get().split('-')
|
|
|
|
|
- if len(parts) == 2 and parts[0].isdigit() and parts[1].isdigit():
|
|
|
|
|
- min_interval = int(parts[0])
|
|
|
|
|
- max_interval = int(parts[1])
|
|
|
|
|
- intervalNum = random.randint(min_interval, max_interval)
|
|
|
|
|
- else:
|
|
|
|
|
- intervalNum = int(self.page_interval_var.get()) if self.page_interval_var.get().isdigit() else 30
|
|
|
|
|
- time.sleep(intervalNum)
|
|
|
|
|
- manager.tap(index, 710, 632, self.log_message)
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- time.sleep(4)
|
|
|
|
|
- # 关闭模拟器
|
|
|
|
|
- self.log_message(f"正在关闭模拟器 {index}...")
|
|
|
|
|
- manager.stop_emulator(index)
|
|
|
|
|
-
|
|
|
|
|
- self.log_message(f"✅ 模拟器 {index} 任务完成")
|
|
|
|
|
-
|
|
|
|
|
- # 如果不是最后一个,等待一段时间再启动下一个
|
|
|
|
|
- if i < len(self.selected_emulators) - 1:
|
|
|
|
|
- wait_time = 10
|
|
|
|
|
- self.log_message(f"等待 {wait_time} 秒后处理下一个模拟器...")
|
|
|
|
|
- for _ in range(wait_time):
|
|
|
|
|
- if self.should_stop:
|
|
|
|
|
- break
|
|
|
|
|
- time.sleep(1)
|
|
|
|
|
|
|
|
|
|
- self.log_message("\n🎉 所有任务执行完毕!")
|
|
|
|
|
|
|
+ # 等待所有线程完成
|
|
|
|
|
+ for thread in threads:
|
|
|
|
|
+ thread.join()
|
|
|
|
|
+
|
|
|
|
|
+ success_count = sum(1 for v in results.values() if v)
|
|
|
|
|
+ self.log_message(f"\n🎉 任务执行完毕!成功: {success_count}/{len(self.selected_emulators)}")
|
|
|
|
|
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
self.log_message(f"❌ 任务执行出错: {e}")
|
|
self.log_message(f"❌ 任务执行出错: {e}")
|
|
@@ -745,87 +853,100 @@ class MuMuAutoGUI:
|
|
|
# 恢复按钮状态
|
|
# 恢复按钮状态
|
|
|
self.root.after(0, self.reset_buttons)
|
|
self.root.after(0, self.reset_buttons)
|
|
|
|
|
|
|
|
|
|
+
|
|
|
def run_task2(self):
|
|
def run_task2(self):
|
|
|
- """执行任务"""
|
|
|
|
|
|
|
+ """执行评价任务(支持并发)"""
|
|
|
try:
|
|
try:
|
|
|
manager = MuMuEmulatorManager(self.mumu_path_var.get())
|
|
manager = MuMuEmulatorManager(self.mumu_path_var.get())
|
|
|
|
|
|
|
|
- for i, emu in enumerate(self.selected_emulators):
|
|
|
|
|
|
|
+ # 获取最大线程数
|
|
|
|
|
+ max_threads = int(self.max_threads_var.get()) if self.max_threads_var.get().isdigit() else 1
|
|
|
|
|
+ self.thread_semaphore = threading.Semaphore(max_threads)
|
|
|
|
|
+ self.log_message(f"📌 最大并发数: {max_threads}")
|
|
|
|
|
+
|
|
|
|
|
+ # 用于存储线程的列表
|
|
|
|
|
+ threads = []
|
|
|
|
|
+ results = {}
|
|
|
|
|
+
|
|
|
|
|
+ def process_emulator(emu):
|
|
|
|
|
+ """处理单个模拟器的函数"""
|
|
|
|
|
+ with self.thread_semaphore:
|
|
|
|
|
+ if self.should_stop:
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ index = emu['index']
|
|
|
|
|
+ self.log_message(f"\n{'='*50}")
|
|
|
|
|
+ self.log_message(f"开始处理模拟器 {index} ({emu['name']})")
|
|
|
|
|
+ self.log_message(f"{'='*50}")
|
|
|
|
|
+
|
|
|
|
|
+ try:
|
|
|
|
|
+ # 启动模拟器
|
|
|
|
|
+ self.log_message(f"正在启动模拟器 {index}...")
|
|
|
|
|
+ if not manager.start_emulator(index):
|
|
|
|
|
+ self.log_message(f"❌ 模拟器 {index} 启动失败")
|
|
|
|
|
+ results[index] = False
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ # 等待就绪
|
|
|
|
|
+ if not manager.wait_for_emulator_ready(index, timeout=180, log_callback=self.log_message):
|
|
|
|
|
+ self.log_message(f"❌ 模拟器 {index} 启动超时")
|
|
|
|
|
+ results[index] = False
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ time.sleep(5)
|
|
|
|
|
+
|
|
|
|
|
+ # 安装APK
|
|
|
|
|
+ if not manager.install_apk(index, self.apk_path_var.get(), self.log_message):
|
|
|
|
|
+ self.log_message(f"❌ APK安装失败")
|
|
|
|
|
+ # results[index] = False
|
|
|
|
|
+ # return
|
|
|
|
|
+
|
|
|
|
|
+ # 打开应用
|
|
|
|
|
+ manager.open_app(index, self.package_name_var.get(), self.log_message)
|
|
|
|
|
+ while manager.get_pixel_color(index, 560, 130, log_callback=self.log_message) != "#EEF8EE" or manager.get_pixel_color(index, 533, 60, log_callback=self.log_message) != "#FFFFFF":
|
|
|
|
|
+ self.log_message(f"模拟器 {index} 等待进入主界面中...")
|
|
|
|
|
+ time.sleep(5)
|
|
|
|
|
+
|
|
|
|
|
+ # 执行操作
|
|
|
|
|
+ self.openBook(manager, index)
|
|
|
|
|
+
|
|
|
|
|
+ # 评价
|
|
|
|
|
+ time.sleep(8)
|
|
|
|
|
+ manager.tap(index, 680, 95, self.log_message)
|
|
|
|
|
+ time.sleep(2)
|
|
|
|
|
+ manager.tap(index, 633, 930, self.log_message)
|
|
|
|
|
+
|
|
|
|
|
+ # 发表
|
|
|
|
|
+ time.sleep(5)
|
|
|
|
|
+ manager.tap(index, 640, 95, self.log_message)
|
|
|
|
|
+
|
|
|
|
|
+ time.sleep(4)
|
|
|
|
|
+ # 关闭模拟器
|
|
|
|
|
+ self.log_message(f"正在关闭模拟器 {index}...")
|
|
|
|
|
+ manager.stop_emulator(index)
|
|
|
|
|
+
|
|
|
|
|
+ self.log_message(f"✅ 模拟器 {index} 任务完成")
|
|
|
|
|
+ results[index] = True
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ self.log_message(f"❌ 模拟器 {index} 执行出错: {e}")
|
|
|
|
|
+ results[index] = False
|
|
|
|
|
+
|
|
|
|
|
+ # 启动所有模拟器任务
|
|
|
|
|
+ for emu in self.selected_emulators:
|
|
|
if self.should_stop:
|
|
if self.should_stop:
|
|
|
- self.log_message("任务已停止")
|
|
|
|
|
break
|
|
break
|
|
|
-
|
|
|
|
|
- while self.is_paused and not self.should_stop:
|
|
|
|
|
- time.sleep(1)
|
|
|
|
|
-
|
|
|
|
|
- index = emu['index']
|
|
|
|
|
- self.log_message(f"\n{'='*50}")
|
|
|
|
|
- self.log_message(f"开始处理模拟器 {index} ({emu['name']})")
|
|
|
|
|
- self.log_message(f"{'='*50}")
|
|
|
|
|
-
|
|
|
|
|
- # 启动模拟器
|
|
|
|
|
- self.log_message(f"正在启动模拟器 {index}...")
|
|
|
|
|
- if not manager.start_emulator(index):
|
|
|
|
|
- self.log_message(f"❌ 模拟器 {index} 启动失败")
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- # 等待就绪
|
|
|
|
|
- if not manager.wait_for_emulator_ready(index, timeout=180, log_callback=self.log_message):
|
|
|
|
|
- self.log_message(f"❌ 模拟器 {index} 启动超时")
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- time.sleep(5)
|
|
|
|
|
-
|
|
|
|
|
- # 安装APK
|
|
|
|
|
- if not manager.install_apk(index, self.apk_path_var.get(), self.log_message):
|
|
|
|
|
- self.log_message(f"❌ APK安装失败")
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- # 打开应用
|
|
|
|
|
- manager.open_app(index, self.package_name_var.get(), self.log_message)
|
|
|
|
|
- time.sleep(40)
|
|
|
|
|
-
|
|
|
|
|
- # 执行操作
|
|
|
|
|
- manager.tap(index, 390, 90, self.log_message)
|
|
|
|
|
|
|
+ thread = threading.Thread(target=process_emulator, args=(emu,))
|
|
|
|
|
+ thread.start()
|
|
|
|
|
+ threads.append(thread)
|
|
|
time.sleep(2)
|
|
time.sleep(2)
|
|
|
- manager.paste_text(index, self.search_content_var.get(), self.log_message)
|
|
|
|
|
- time.sleep(2)
|
|
|
|
|
- manager.tap(index, 655, 92, self.log_message)
|
|
|
|
|
-
|
|
|
|
|
- # 进入书目
|
|
|
|
|
- time.sleep(5)
|
|
|
|
|
- manager.tap(index, 355, 333, self.log_message)
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
- # 评价
|
|
|
|
|
- # time.sleep(2)
|
|
|
|
|
- # manager.tap(index, 360, 690, self.log_message)
|
|
|
|
|
- time.sleep(8)
|
|
|
|
|
- manager.tap(index, 680, 95, self.log_message)
|
|
|
|
|
- time.sleep(2)
|
|
|
|
|
- manager.tap(index, 633, 930, self.log_message)
|
|
|
|
|
-
|
|
|
|
|
- # 发表
|
|
|
|
|
- time.sleep(5)
|
|
|
|
|
- manager.tap(index, 640, 95, self.log_message)
|
|
|
|
|
-
|
|
|
|
|
- time.sleep(4)
|
|
|
|
|
- # 关闭模拟器
|
|
|
|
|
- self.log_message(f"正在关闭模拟器 {index}...")
|
|
|
|
|
- manager.stop_emulator(index)
|
|
|
|
|
-
|
|
|
|
|
- self.log_message(f"✅ 模拟器 {index} 任务完成")
|
|
|
|
|
-
|
|
|
|
|
- # 如果不是最后一个,等待一段时间再启动下一个
|
|
|
|
|
- if i < len(self.selected_emulators) - 1:
|
|
|
|
|
- wait_time = 10
|
|
|
|
|
- self.log_message(f"等待 {wait_time} 秒后处理下一个模拟器...")
|
|
|
|
|
- for _ in range(wait_time):
|
|
|
|
|
- if self.should_stop:
|
|
|
|
|
- break
|
|
|
|
|
- time.sleep(1)
|
|
|
|
|
|
|
|
|
|
- self.log_message("\n🎉 所有任务执行完毕!")
|
|
|
|
|
|
|
+ # 等待所有线程完成
|
|
|
|
|
+ for thread in threads:
|
|
|
|
|
+ thread.join()
|
|
|
|
|
+
|
|
|
|
|
+ success_count = sum(1 for v in results.values() if v)
|
|
|
|
|
+ self.log_message(f"\n🎉 任务执行完毕!成功: {success_count}/{len(self.selected_emulators)}")
|
|
|
|
|
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
self.log_message(f"❌ 任务执行出错: {e}")
|
|
self.log_message(f"❌ 任务执行出错: {e}")
|
|
@@ -855,6 +976,6 @@ class MuMuAutoGUI:
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if __name__ == "__main__":
|
|
|
- if datetime.now() < datetime(2026, 4, 25):
|
|
|
|
|
|
|
+ if datetime.now() < datetime(2026, 5, 25):
|
|
|
app = MuMuAutoGUI()
|
|
app = MuMuAutoGUI()
|
|
|
app.run()
|
|
app.run()
|