PUGE пре 1 месец
родитељ
комит
5fddecd083
2 измењених фајлова са 752 додато и 191 уклоњено
  1. 744 191
      MuMu.py
  2. 8 0
      mumu_config.ini

+ 744 - 191
MuMu.py

@@ -1,8 +1,13 @@
+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"):
@@ -16,7 +21,6 @@ class MuMuEmulatorManager:
         result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
         
         if result.returncode != 0:
-            print(f"获取列表失败: {result.stderr}")
             return []
         
         try:
@@ -25,283 +29,832 @@ class MuMuEmulatorManager:
             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 as e:
-            print(f"JSON解析失败: {e}")
+        except json.JSONDecodeError:
             return []
     
     def start_emulator(self, index):
-        """启动指定索引的模拟器 - 使用正确的 launch 命令"""
-        print(f"正在启动模拟器 {index}...")
-        
-        # 正确的命令格式:control -v <index> launch
+        """启动指定索引的模拟器"""
         cmd = [self.manager_path, "control", "-v", str(index), "launch"]
-        print(f"执行命令: {' '.join(cmd)}")
-        
         result = subprocess.run(cmd, capture_output=True, text=True, encoding='utf-8')
-        
-        if result.returncode == 0:
-            print(f"✅ 模拟器 {index} 启动命令已发送")
-            return True
-        else:
-            print(f"❌ 启动失败")
-            if result.stderr:
-                print(f"错误信息: {result.stderr}")
-            if result.stdout:
-                print(f"输出: {result.stdout}")
-            return False
+        return result.returncode == 0
     
-    def wait_for_emulator_ready(self, index, timeout=120, check_interval=3):
-        """等待模拟器启动完成(Android 系统就绪)"""
-        print(f"\n等待模拟器 {index} 启动完成...")
+    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)
-                    # 检查 Android 是否已启动
                     if data.get('is_android_started') == True:
-                        elapsed = time.time() - start_time
-                        print(f"✅ 模拟器 {index} 已就绪!耗时: {elapsed:.1f} 秒")
                         return True
-                    elif data.get('is_process_started') == True:
-                        print(f"⏳ 进程已启动,等待 Android 系统... ({int(time.time() - start_time)}秒)")
-                    else:
-                        print(f"⏳ 等待进程启动... ({int(time.time() - start_time)}秒)")
                 except:
                     pass
             
+            if log_callback:
+                log_callback(f"等待模拟器 {index} 启动... ({int(time.time() - start_time)}秒)")
             time.sleep(check_interval)
         
-        print(f"❌ 超时!模拟器 {index} 在 {timeout} 秒内未就绪")
         return False
     
-    def get_adb_port(self, index):
-        """获取模拟器的 ADB 端口"""
-        # 通过 MuMuManager 获取
-        cmd = [self.manager_path, "info", "-v", str(index)]
+    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')
-        
-        if result.returncode == 0:
-            try:
-                data = json.loads(result.stdout)
-                # 注意:从之前的输出看,info 命令没有直接返回 adb_port
-                # 可能需要通过其他方式获取
-                pass
-            except:
-                pass
-        
-        # 使用 MuMu 默认端口规律:16384 + 32 * index
-        default_port = 16384 + 32 * int(index)
-        print(f"估算 ADB 端口: {default_port}")
-        return default_port
+        return result.returncode == 0
     
-    def install_apk(self, index, apk_path):
-        """通过 ADB 安装 APK 到模拟器"""
-        # 检查 APK 是否存在
+    def install_apk(self, index, apk_path, log_callback=None):
+        """安装APK"""
         if not os.path.exists(apk_path):
-            print(f"❌ 错误:APK 文件不存在: {apk_path}")
+            if log_callback:
+                log_callback(f"❌ APK文件不存在: {apk_path}")
             return False
         
-        # 获取 ADB 端口
-        adb_port = self.get_adb_port(index)
+        adb_port = 16384 + 32 * int(index)
         target_device = f"127.0.0.1:{adb_port}"
         
-        # 连接 ADB
-        print(f"\n连接 ADB {target_device}...")
-        connect_result = subprocess.run(f"adb connect {target_device}", 
-                                    shell=True, capture_output=True, text=True)
-        print(connect_result.stdout.strip())
-        
-        # 等待连接稳定
+        # 连接ADB
+        subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
         time.sleep(2)
         
-        # 检查设备连接
-        devices_result = subprocess.run("adb devices", shell=True, capture_output=True, text=True)
-        print(f"\n当前设备列表:\n{devices_result.stdout}")
-        
-        # 检查是否已安装 com.dragon.read
-        print(f"\n检查是否已安装 com.dragon.read...")
+        # 检查是否已安装
         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:
-            print("✅ com.dragon.read 已安装,跳过安装步骤")
+            if log_callback:
+                log_callback(f"✅ com.dragon.read 已安装,跳过安装步骤")
             return True
         
-        # 安装 APK
-        print(f"\n正在安装 APK: {os.path.basename(apk_path)}")
+        # 安装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)
         
-        if "Success" in result.stdout:
-            print("✅ APK 安装成功!")
-            return True
-        else:
-            print("❌ APK 安装失败")
-            print(f"输出: {result.stdout}")
-            if result.stderr:
-                print(f"错误: {result.stderr}")
-            return False
+        return "Success" in result.stdout
     
-    def display_emulator_list(self):
-        """显示模拟器列表"""
-        emulators = self.get_emulator_list()
-        
-        if not emulators:
-            print("未找到任何模拟器")
-            return
-        
-        print(f"\n{'='*60}")
-        print(f"找到 {len(emulators)} 个模拟器:")
-        print(f"{'='*60}\n")
-        
-        for i, emu in enumerate(emulators, 1):
-            status = "🟢 运行中" if emu.get('is_process_started') else "🔴 未运行"
-            android_status = "✅ 已启动" if emu.get('is_android_started') else "⏸️ 未启动"
-            print(f"{i}. 索引 {emu.get('index')}: {emu.get('name')}")
-            print(f"   进程状态: {status}")
-            print(f"   Android: {android_status}")
-            print()
-    def open_app(self, index, package_name="com.dragon.read"):
-        """打开指定包名的应用"""
-        # 获取 ADB 端口
-        adb_port = self.get_adb_port(index)
+    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}"
         
-        # 连接 ADB
-        print(f"\n连接 ADB {target_device}...")
         subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
         time.sleep(1)
         
-        # 打开应用(使用 monkey 命令,最通用)
-        print(f"\n正在打开应用: {package_name}")
         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:
-            print(f"✅ 应用已打开: {package_name}")
+            if log_callback:
+                log_callback(f"✅ 已打开应用: {package_name}")
             return True
-        else:
-            print(f"❌ 打开失败")
-            print(f"输出: {result.stdout}")
-            if result.stderr:
-                print(f"错误: {result.stderr}")
-            return False
-    def paste_text(self, index, text):
-        """将文字放到系统剪贴板并粘贴"""
-        # 获取 ADB 端口
-        adb_port = self.get_adb_port(index)
+        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}"
         
-        # 连接 ADB
         subprocess.run(f"adb connect {target_device}", shell=True, capture_output=True)
         time.sleep(0.5)
-        # 1. 复制到电脑剪贴板
-        """使用 Windows clip 命令复制到剪贴板"""
+        
+        # 复制到剪贴板
         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)
         
-        # 执行粘贴 (Ctrl+V)
+        # 执行粘贴
         subprocess.run(f"adb -s {target_device} shell input keyevent 279", shell=True)
         
-        print(f"✅ 已粘贴: {text[:50]}{'...' if len(text) > 50 else ''}")
+        if log_callback:
+            log_callback(f"✅ 已粘贴: {text[:50]}{'...' if len(text) > 50 else ''}")
         return True
-    def tap(self, index, x, y):
-        """点击指定坐标"""
-        # 获取 ADB 端口
-        adb_port = self.get_adb_port(index)
+    
+    def tap(self, index, x, y, log_callback=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)
         
-        # 点击坐标
         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:
-            print(f"✅ 点击坐标 ({x}, {y})")
+            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:
-            print(f"❌ 点击失败: {result.stderr}")
-            return False
-def main():
-    # 配置
-    MANAGER_PATH = r"D:\MuMuPlayer\nx_main\MuMuManager.exe"
-    APK_PATH = r"fanqie.apk"  # 请修改为你的 APK 实际路径
-    TARGET_INDEX = 1  # 第二个模拟器的索引(索引从0开始,1是第二个)
+            self.config['Settings'] = {
+                'mumu_path': r'D:\MuMuPlayer\nx_main\MuMuManager.exe',
+                'apk_path': 'fanqie.apk',
+                'package_name': 'com.dragon.read',
+                'search_content': '玄幻战神:开局就得到大佬的守护'
+            }
     
-    # 创建管理器实例
-    try:
-        manager = MuMuEmulatorManager(MANAGER_PATH)
-    except FileNotFoundError as e:
-        print(e)
-        return
+    def save_config(self):
+        """保存配置文件"""
+        with open(self.config_file, 'w', encoding='utf-8') as f:
+            self.config.write(f)
     
-    # 1. 显示所有模拟器列表
-    print("步骤1: 获取模拟器列表")
-    manager.display_emulator_list()
+    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')
+
     
-    # 2. 启动第二个模拟器(索引为1)
-    print(f"\n步骤2: 启动模拟器 {TARGET_INDEX}")
-    if not manager.start_emulator(TARGET_INDEX):
-        print("启动失败,退出")
-        return
+    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("✅ 配置已保存")
     
-    # 3. 等待模拟器启动完成
-    print(f"\n步骤3: 等待模拟器就绪")
-    if not manager.wait_for_emulator_ready(TARGET_INDEX, timeout=180):
-        print("模拟器未能在规定时间内就绪,退出")
-        return
-    time.sleep(5)
-    # 4. 安装 APK
-    print(f"\n步骤4: 安装 APK")
-    if manager.install_apk(TARGET_INDEX, APK_PATH):
-        print("\n🎉 全部步骤完成!")
-        manager.open_app(TARGET_INDEX, package_name="com.dragon.read")
-        time.sleep(40)
-        manager.tap(TARGET_INDEX, 390, 90)  # 点击输入框坐标(示例)
-        time.sleep(2)
-        manager.paste_text(TARGET_INDEX, "玄幻战神:开局就得到大佬的守护")
-        time.sleep(2)
-        manager.tap(TARGET_INDEX, 655, 92)
-        # 进入数目
-        time.sleep(5)
-        manager.tap(TARGET_INDEX, 355, 333)
-        ind = 0
-        while ind < 10:
-            time.sleep(30)
-            manager.tap(TARGET_INDEX, 710, 632)
-            ind += 1
-        # 评价
-        time.sleep(2)
-        manager.tap(TARGET_INDEX, 360, 690)
-        time.sleep(2)
-        manager.tap(TARGET_INDEX, 680, 95)
-        time.sleep(2)
-        manager.tap(TARGET_INDEX, 633, 930)
-        # 发表
-        time.sleep(2)
-        manager.tap(TARGET_INDEX, 640, 95)
-    else:
-        print("\n❌ 运行失败")
+    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__":
-    main()
+    if datetime.now() < datetime(2026, 4, 25):
+        app = MuMuAutoGUI()
+        app.run()

+ 8 - 0
mumu_config.ini

@@ -0,0 +1,8 @@
+[Settings]
+mumu_path = D:\MuMuPlayer\nx_main\MuMuManager.exe
+apk_path = fanqie.apk
+package_name = com.dragon.read
+search_content = 玄幻战神:开局就得到大佬的守护
+page_count_var = 5
+page_interval_var = 30-40
+