| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860 |
- import random
- import subprocess
- import json
- import time
- import os
- import tkinter as tk
- from tkinter import ttk, scrolledtext, messagebox, filedialog
- import threading
- import configparser
- from datetime import datetime
- class MuMuEmulatorManager:
- def __init__(self, manager_path=r"D:\MuMuPlayer\nx_main\MuMuManager.exe"):
- self.manager_path = manager_path
- if not os.path.exists(manager_path):
- raise FileNotFoundError(f"找不到 MuMuManager.exe: {manager_path}")
-
- def get_emulator_list(self):
- """获取所有模拟器列表"""
- cmd = [self.manager_path, "info", "-v", "all"]
- result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
-
- if result.returncode != 0:
- return []
-
- try:
- data = json.loads(result.stdout)
- emulators = []
- for key, value in data.items():
- if isinstance(value, dict):
- value['index'] = key
- # 计算ADB端口
- value['adb_port'] = 16384 + 32 * int(key)
- emulators.append(value)
- return emulators
- except json.JSONDecodeError:
- return []
-
- def start_emulator(self, index):
- """启动指定索引的模拟器"""
- cmd = [self.manager_path, "control", "-v", str(index), "launch"]
- result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
- return result.returncode == 0
-
- def wait_for_emulator_ready(self, index, timeout=120, check_interval=3, log_callback=None):
- """等待模拟器启动完成"""
- start_time = time.time()
-
- while time.time() - start_time < timeout:
- 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)
- if data.get('is_android_started') == True:
- return True
- except:
- pass
-
- if log_callback:
- log_callback(f"等待模拟器 {index} 启动... ({int(time.time() - start_time)}秒)")
- time.sleep(check_interval)
-
- return False
-
- def stop_emulator(self, index):
- """关闭模拟器"""
- cmd = [self.manager_path, "control", "-v", str(index), "shutdown"]
- result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
- return result.returncode == 0
-
- def install_apk(self, index, apk_path, log_callback=None):
- """安装APK"""
- if not os.path.exists(apk_path):
- if log_callback:
- log_callback(f"❌ APK文件不存在: {apk_path}")
- return False
-
- adb_port = 16384 + 32 * int(index)
- target_device = f"127.0.0.1:{adb_port}"
-
- # 连接ADB
- subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
- time.sleep(2)
-
- # 检查是否已安装
- check_cmd = f"adb -s {target_device} shell pm list packages | findstr \"com.dragon.read\""
- check_result = subprocess.run(check_cmd, shell=True, capture_output=True, text=True)
-
- if "com.dragon.read" in check_result.stdout:
- if log_callback:
- log_callback(f"✅ com.dragon.read 已安装,跳过安装步骤")
- return True
-
- # 安装APK
- if log_callback:
- log_callback(f"正在安装APK: {os.path.basename(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)
-
- return "Success" in result.stdout
-
- def open_app(self, index, package_name, log_callback=None):
- """打开应用"""
- adb_port = 16384 + 32 * int(index)
- target_device = f"127.0.0.1:{adb_port}"
-
- subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
- time.sleep(1)
-
- cmd = f"adb -s {target_device} shell monkey -p {package_name} -c android.intent.category.LAUNCHER 1"
- result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
-
- if "Events injected" in result.stdout or result.returncode == 0:
- if log_callback:
- log_callback(f"✅ 已打开应用: {package_name}")
- return True
- return False
-
- def paste_text(self, index, text, log_callback=None):
- """粘贴文字"""
- adb_port = 16384 + 32 * int(index)
- target_device = f"127.0.0.1:{adb_port}"
-
- 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)
-
- # 执行粘贴
- subprocess.run(f"adb -s {target_device} shell input keyevent 279", shell=True)
-
- if log_callback:
- log_callback(f"✅ 已粘贴: {text[:50]}{'...' if len(text) > 50 else ''}")
- return True
-
- def tap(self, index, x, y, log_callback=None):
- """点击坐标"""
- adb_port = 16384 + 32 * int(index)
- target_device = f"127.0.0.1:{adb_port}"
-
- subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
-
- cmd = f"adb -s {target_device} shell input tap {x} {y}"
- result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
-
- if result.returncode == 0:
- if log_callback:
- log_callback(f"✅ 点击坐标 ({x}, {y})")
- return True
- return False
-
- 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)
- target_device = f"127.0.0.1:{adb_port}"
-
- # 连接ADB
- subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
- time.sleep(0.5)
-
- try:
- # 获取屏幕分辨率
- 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)
-
- if resolution_result.stdout:
- # 解析分辨率,格式如 "Physical size: 720x1280"
- import re
- match = re.search(r'(\d+)x(\d+)', resolution_result.stdout)
- if match:
- screen_width = int(match.group(1))
- screen_height = int(match.group(2))
- else:
- screen_width = 720
- screen_height = 1280
- else:
- screen_width = 720
- screen_height = 1280
-
- if log_callback:
- log_callback(f"📱 屏幕分辨率: {screen_width}x{screen_height}")
-
- # 使用 screencap -p 获取 PNG 格式,然后通过 Python 解析
- # 创建本地临时文件
- temp_local_file = f"temp_screenshot_{int(time.time())}.png"
-
- # 截图并保存到本地
- screenshot_cmd = f"adb -s {target_device} exec-out screencap -p > {temp_local_file}"
- subprocess.run(screenshot_cmd, shell=True, capture_output=True, text=True)
- time.sleep(0.3)
-
- # 检查文件是否存在且不为空
- if os.path.exists(temp_local_file) and os.path.getsize(temp_local_file) > 0:
- try:
- from PIL import Image
-
- # 打开图片
- img = Image.open(temp_local_file)
-
- # 确保坐标在范围内
- width, height = img.size
- if x < 0 or x >= width or y < 0 or y >= height:
- if log_callback:
- log_callback(f"❌ 坐标({x},{y})超出屏幕范围 {width}x{height}")
- os.remove(temp_local_file)
- return None
-
- # 获取像素颜色
- pixel = img.getpixel((x, y))
-
- # 转换为十六进制颜色值
- if isinstance(pixel, tuple):
- if len(pixel) >= 3:
- r, g, b = pixel[0], pixel[1], pixel[2]
- else:
- r, g, b = pixel, pixel, pixel
- else:
- r = g = b = pixel
-
- color = f"#{r:02X}{g:02X}{b:02X}"
-
- # 清理临时文件
- os.remove(temp_local_file)
-
- if log_callback:
- log_callback(f"🎨 坐标({x},{y}) 颜色: {color}")
- return color
-
- except ImportError:
- if log_callback:
- log_callback("❌ 请先安装PIL库: pip install Pillow")
- # 清理临时文件
- if os.path.exists(temp_local_file):
- os.remove(temp_local_file)
- return None
- except Exception as e:
- if log_callback:
- log_callback(f"❌ 解析图片失败: {e}")
- # 清理临时文件
- if os.path.exists(temp_local_file):
- os.remove(temp_local_file)
- return None
- else:
- if log_callback:
- log_callback("❌ 截图失败")
- return None
-
- except Exception as e:
- if log_callback:
- log_callback(f"❌ 获取颜色失败: {e}")
-
- return None
- class MuMuAutoGUI:
- def __init__(self):
- self.root = tk.Tk()
- self.root.title("MuMu模拟器自动化工具")
- self.root.geometry("700x360")
-
- # 配置文件
- self.config_file = "mumu_config.ini"
- self.config = configparser.ConfigParser()
- self.load_config()
-
- # 运行状态
- self.is_running = False
- self.is_paused = False
- self.should_stop = False
- self.current_thread = None
- self.selected_emulators = []
-
- self.load_btn = None
- self.start_read_btn = None
- self.start_comment_btn = None
-
- # 创建界面
- self.create_widgets()
-
- # 加载保存的配置
- self.load_settings()
-
- def load_config(self):
- """加载配置文件"""
- if os.path.exists(self.config_file):
- self.config.read(self.config_file, encoding='utf-8')
- else:
- self.config['Settings'] = {
- 'mumu_path': r'D:\MuMuPlayer\nx_main\MuMuManager.exe',
- 'apk_path': 'fanqie.apk',
- 'package_name': 'com.dragon.read',
- 'search_content': '玄幻战神:开局就得到大佬的守护'
- }
-
- def save_config(self):
- """保存配置文件"""
- with open(self.config_file, 'w', encoding='utf-8') as f:
- self.config.write(f)
-
- def load_settings(self):
- """加载设置到界面"""
- self.mumu_path_var.set(self.config['Settings']['mumu_path'] if 'mumu_path' in self.config['Settings'] else '')
- self.apk_path_var.set(self.config['Settings']['apk_path'] if 'apk_path' in self.config['Settings'] else '')
- self.package_name_var.set(self.config['Settings']['package_name'] if 'package_name' in self.config['Settings'] else '')
- self.search_content_var.set(self.config['Settings']['search_content'] if 'search_content' in self.config['Settings'] else '盗墓笔记')
- self.page_count_var.set(self.config['Settings']['page_count_var'] if 'page_count_var' in self.config['Settings'] else '10-30')
- self.page_interval_var.set(self.config['Settings']['page_interval_var'] if 'page_interval_var' in self.config['Settings'] else '5')
- self.max_threads_var.set(self.config['Settings']['max_threads_var'] if 'max_threads_var' in self.config['Settings'] else '1')
-
- def save_settings(self):
- """保存界面设置到文件"""
- self.config['Settings']['mumu_path'] = self.mumu_path_var.get()
- self.config['Settings']['apk_path'] = self.apk_path_var.get()
- self.config['Settings']['package_name'] = self.package_name_var.get()
- self.config['Settings']['search_content'] = self.search_content_var.get()
- self.config['Settings']['page_count_var'] = self.page_count_var.get()
- self.config['Settings']['page_interval_var'] = self.page_interval_var.get()
- self.config['Settings']['max_threads_var'] = self.max_threads_var.get()
-
- self.save_config()
- self.log_message("✅ 配置已保存")
-
- def create_widgets(self):
- """创建界面组件"""
- # 创建选项卡
- self.notebook = ttk.Notebook(self.root)
- self.notebook.pack(fill='both', expand=True, padx=5, pady=5)
-
- # 配置选项卡
- self.create_config_tab()
-
- # 任务选项卡
- self.create_task_tab()
-
- # 日志选项卡
- self.create_log_tab()
-
- def create_config_tab(self):
- """创建配置选项卡"""
- config_frame = ttk.Frame(self.notebook)
- self.notebook.add(config_frame, text="配置")
-
- # 创建滚动框架
- canvas = tk.Canvas(config_frame)
- scrollbar = ttk.Scrollbar(config_frame, orient="vertical", command=canvas.yview)
- scrollable_frame = ttk.Frame(canvas)
-
- scrollable_frame.bind(
- "<Configure>",
- lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
- )
-
- canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
- canvas.configure(yscrollcommand=scrollbar.set)
-
- # 配置项
- row = 0
-
- # MuMu路径
- ttk.Label(scrollable_frame, text="MuMuManager路径:").grid(row=row, column=0, sticky='w', padx=10, pady=5)
- self.mumu_path_var = tk.StringVar()
- mumu_entry = ttk.Entry(scrollable_frame, textvariable=self.mumu_path_var, width=60)
- mumu_entry.grid(row=row, column=1, padx=10, pady=5)
- ttk.Button(scrollable_frame, text="浏览", command=self.browse_mumu_path).grid(row=row, column=2, padx=5, pady=5)
- row += 1
-
- # APK路径
- ttk.Label(scrollable_frame, text="APK文件路径:").grid(row=row, column=0, sticky='w', padx=10, pady=5)
- self.apk_path_var = tk.StringVar()
- apk_entry = ttk.Entry(scrollable_frame, textvariable=self.apk_path_var, width=60)
- apk_entry.grid(row=row, column=1, padx=10, pady=5)
- ttk.Button(scrollable_frame, text="浏览", command=self.browse_apk_path).grid(row=row, column=2, padx=5, pady=5)
- row += 1
-
- # 包名
- ttk.Label(scrollable_frame, text="应用包名:").grid(row=row, column=0, sticky='w', padx=10, pady=5)
- self.package_name_var = tk.StringVar()
- ttk.Entry(scrollable_frame, textvariable=self.package_name_var, width=40).grid(row=row, column=1, sticky='w', padx=10, pady=5)
- row += 1
-
- # 搜索内容
- ttk.Label(scrollable_frame, text="搜索内容:").grid(row=row, column=0, sticky='w', padx=10, pady=5)
- self.search_content_var = tk.StringVar()
- ttk.Entry(scrollable_frame, textvariable=self.search_content_var, width=60).grid(row=row, column=1, padx=10, pady=5)
- row += 1
- # 翻页间隔
- ttk.Label(scrollable_frame, text="翻页间隔(秒):").grid(row=row, column=0, sticky='w', padx=10, pady=5)
- self.page_interval_var = tk.StringVar()
- ttk.Entry(scrollable_frame, textvariable=self.page_interval_var, width=60).grid(row=row, column=1, padx=10, pady=5)
- row += 1
- # 阅读页数
- ttk.Label(scrollable_frame, text="阅读页数:").grid(row=row, column=0, sticky='w', padx=10, pady=5)
- self.page_count_var = tk.StringVar()
- ttk.Entry(scrollable_frame, textvariable=self.page_count_var, width=60).grid(row=row, column=1, padx=10, pady=5)
- row += 1
- # 最大线程
- ttk.Label(scrollable_frame, text="最大线程:").grid(row=row, column=0, sticky='w', padx=10, pady=5)
- self.max_threads_var = tk.StringVar()
- ttk.Entry(scrollable_frame, textvariable=self.max_threads_var, width=60).grid(row=row, column=1, padx=10, pady=5)
- row += 1
-
- # 保存按钮
- ttk.Button(scrollable_frame, text="保存配置", command=self.save_settings).grid(row=row, column=0, columnspan=3, pady=20)
-
- canvas.pack(side="left", fill="both", expand=True)
- scrollbar.pack(side="right", fill="y")
-
- def create_task_tab(self):
- """创建任务选项卡"""
- task_frame = ttk.Frame(self.notebook)
- self.notebook.add(task_frame, text="任务")
-
- # 上部:模拟器列表
- list_frame = ttk.LabelFrame(task_frame, text="任务控制")
- list_frame.pack(fill='both', expand=True, padx=5, pady=5)
-
- # 按钮栏
- button_frame = ttk.Frame(list_frame)
- button_frame.pack(fill='x', padx=5, pady=5)
-
- self.load_btn = ttk.Button(button_frame, text="读取模拟器", command=self.load_emulators)
- self.load_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.pack(side='left', padx=10)
- self.start_comment_btn = ttk.Button(button_frame, text="开始评价", command=self.start_task2, width=10)
- self.start_comment_btn.pack(side='left', padx=10)
-
- self.pause_btn = ttk.Button(button_frame, text="暂停", command=self.pause_task, width=10, state='disabled')
- self.pause_btn.pack(side='left', padx=10)
-
- self.stop_btn = ttk.Button(button_frame, text="停止", command=self.stop_task, width=10, state='disabled')
- self.stop_btn.pack(side='left', padx=10)
-
- # 模拟器列表(带复选框)
- tree_frame = ttk.Frame(list_frame)
- tree_frame.pack(fill='both', expand=True, padx=5, pady=5)
-
- # 创建Treeview
- columns = ("选择", "索引", "名称", "ADB端口", "状态")
- 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("ADB端口", text="ADB端口")
- self.emulator_tree.heading("状态", text="状态")
-
- # 设置列宽
- self.emulator_tree.column("选择", width=50)
- self.emulator_tree.column("索引", width=50)
- self.emulator_tree.column("名称", width=150)
- self.emulator_tree.column("ADB端口", width=80)
- self.emulator_tree.column("状态", width=100)
-
- # 添加滚动条
- vsb = ttk.Scrollbar(tree_frame, orient="vertical", command=self.emulator_tree.yview)
- self.emulator_tree.configure(yscrollcommand=vsb.set)
-
- self.emulator_tree.pack(side='left', fill='both', expand=True)
- vsb.pack(side='right', fill='y')
-
- # 绑定双击选择
- self.emulator_tree.bind('<Button-1>', self.on_tree_click)
-
- def create_log_tab(self):
- """创建日志选项卡"""
- log_frame = ttk.Frame(self.notebook)
- self.notebook.add(log_frame, text="日志")
-
- # 日志文本框
- self.log_text = scrolledtext.ScrolledText(log_frame, wrap=tk.WORD, height=20)
- self.log_text.pack(fill='both', expand=True, padx=5, pady=5)
-
- # 清空按钮
- btn_frame = ttk.Frame(log_frame)
- btn_frame.pack(fill='x', padx=5, pady=5)
- ttk.Button(btn_frame, text="清空日志", command=self.clear_log).pack(side='right')
-
- def browse_mumu_path(self):
- """浏览MuMuManager路径"""
- path = filedialog.askopenfilename(title="选择MuMuManager.exe", filetypes=[("Executable", "*.exe")])
- if path:
- self.mumu_path_var.set(path)
-
- def browse_apk_path(self):
- """浏览APK文件"""
- path = filedialog.askopenfilename(title="选择APK文件", filetypes=[("APK", "*.apk")])
- if path:
- self.apk_path_var.set(path)
-
- def load_emulators(self):
- """加载模拟器列表"""
- try:
- manager = MuMuEmulatorManager(self.mumu_path_var.get())
- emulators = manager.get_emulator_list()
-
- # 清空现有列表
- for item in self.emulator_tree.get_children():
- self.emulator_tree.delete(item)
-
- # 添加模拟器
- for emu in emulators:
- 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.log_message(f"已加载 {len(emulators)} 个模拟器")
- except Exception as e:
- self.log_message(f"加载模拟器失败: {e}")
-
- def on_tree_click(self, event):
- """处理列表点击选择"""
- region = self.emulator_tree.identify_region(event.x, event.y)
- if region == "cell":
- column = self.emulator_tree.identify_column(event.x)
- if column == "#1": # 选择列
- item = self.emulator_tree.identify_row(event.y)
- if item:
- values = self.emulator_tree.item(item, 'values')
- current = values[0]
- new_value = "☑" if current == "□" else "□"
- self.emulator_tree.item(item, values=(new_value, values[1], values[2], values[3], values[4]))
- def get_selected_emulators(self):
- """获取选中的模拟器"""
- selected = []
- for item in self.emulator_tree.get_children():
- values = self.emulator_tree.item(item, 'values')
- if values[0] == "☑":
- selected.append({
- 'index': values[1],
- 'name': values[2],
- 'adb_port': values[3]
- })
- return selected
-
- def log_message(self, message):
- """添加日志"""
- timestamp = datetime.now().strftime("%H:%M:%S")
- self.log_text.insert(tk.END, f"[{timestamp}] {message}\n")
- self.log_text.see(tk.END)
- self.root.update()
-
- def start_task(self):
- """开始阅读任务"""
- selected = self.get_selected_emulators()
- if not selected:
- messagebox.showwarning("警告", "请至少选择一个模拟器")
- return
-
- self.selected_emulators = selected
- self.is_running = True
- self.is_paused = False
- self.should_stop = False
-
- # 禁用相关按钮
- self.load_btn.config(state='disabled')
- self.start_read_btn.config(state='disabled')
- self.start_comment_btn.config(state='disabled')
- self.pause_btn.config(state='normal')
- self.stop_btn.config(state='normal')
-
- # 在新线程中运行任务
- self.current_thread = threading.Thread(target=self.run_task, daemon=True)
- self.current_thread.start()
- def start_task2(self):
- """开始评价任务"""
- selected = self.get_selected_emulators()
- if not selected:
- messagebox.showwarning("警告", "请至少选择一个模拟器")
- return
-
- self.selected_emulators = selected
- self.is_running = True
- self.is_paused = False
- self.should_stop = False
-
- # 禁用相关按钮
- self.load_btn.config(state='disabled')
- self.start_read_btn.config(state='disabled')
- self.start_comment_btn.config(state='disabled')
- self.pause_btn.config(state='normal')
- self.stop_btn.config(state='normal')
-
- # 在新线程中运行评价任务
- self.current_thread = threading.Thread(target=self.run_task2, daemon=True)
- self.current_thread.start()
- def pause_task(self):
- """暂停任务"""
- if self.is_running and not self.is_paused:
- self.is_paused = True
- self.pause_btn.config(text="继续")
- self.log_message("⏸ 任务已暂停")
- elif self.is_running and self.is_paused:
- self.is_paused = False
- self.pause_btn.config(text="暂停")
- self.log_message("▶️ 任务已继续")
-
- def stop_task(self):
- """停止任务"""
- if self.is_running:
- self.should_stop = True
- self.is_running = False
- self.is_paused = False
- self.log_message("⏹ 正在停止任务...")
-
- def run_task(self):
- """执行任务"""
- try:
- manager = MuMuEmulatorManager(self.mumu_path_var.get())
-
- for i, emu in enumerate(self.selected_emulators):
- if self.should_stop:
- self.log_message("任务已停止")
- 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(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🎉 所有任务执行完毕!")
-
- except Exception as e:
- self.log_message(f"❌ 任务执行出错: {e}")
- finally:
- self.is_running = False
- self.is_paused = False
- self.pause_btn.config(text="暂停")
-
- # 恢复按钮状态
- self.root.after(0, self.reset_buttons)
-
- def run_task2(self):
- """执行任务"""
- try:
- manager = MuMuEmulatorManager(self.mumu_path_var.get())
-
- for i, emu in enumerate(self.selected_emulators):
- if self.should_stop:
- self.log_message("任务已停止")
- 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)
- 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🎉 所有任务执行完毕!")
-
- except Exception as e:
- self.log_message(f"❌ 任务执行出错: {e}")
- finally:
- self.is_running = False
- self.is_paused = False
- self.pause_btn.config(text="暂停")
-
- # 恢复按钮状态
- self.root.after(0, self.reset_buttons)
- def reset_buttons(self):
- """重置按钮状态"""
- self.load_btn.config(state='normal')
- self.start_read_btn.config(state='normal')
- self.start_comment_btn.config(state='normal')
- self.pause_btn.config(state='disabled', text="暂停")
- self.stop_btn.config(state='disabled')
-
- def clear_log(self):
- """清空日志"""
- self.log_text.delete(1.0, tk.END)
-
- def run(self):
- """运行GUI"""
- self.root.mainloop()
- if __name__ == "__main__":
- if datetime.now() < datetime(2026, 4, 25):
- app = MuMuAutoGUI()
- app.run()
|