MJ提示词自动批处理GUI版

import tkinter as tk
from tkinter import filedialog, ttk, messagebox
from DrissionPage import ChromiumPage, ChromiumOptions
import pandas as pd
import time
import os
import json
from DrissionPage.common import Keys
from threading import Thread
import random


class MJInputApp:
    def __init__(self, root):
        self.root = root
        self.root.title("MidJourney 提示词输入工具")
        self.root.geometry("600x500")
        self.root.resizable(True, True)

        # 默认配置
        self.config = {
            "chrome_path": r"C:\Users\Administrator\AppData\Local\Google\Chrome\Bin\chrome.exe",
            "debugger_address": "127.0.0.1:9222",
            "excel_path": "data.xlsx",
            "min_wait": 3.5,
            "max_wait": 5.5
        }

        # 尝试加载配置文件
        self.load_config()

        # 创建主框架
        self.main_frame = ttk.Frame(root, padding="10")
        self.main_frame.pack(fill=tk.BOTH, expand=True)

        # 创建浏览器配置指南按钮(醒目的红色按钮)
        self.create_browser_guide_button()

        # 创建设置区域
        self.create_settings_frame()

        # 创建日志区域
        self.create_log_frame()

        # 创建按钮区域
        self.create_button_frame()

        # 运行状态
        self.running = False
        self.thread = None

    def create_browser_guide_button(self):
        """创建一个醒目的浏览器配置指南按钮"""
        guide_frame = ttk.Frame(self.main_frame)
        guide_frame.pack(fill=tk.X, pady=5)
        
        # 使用原生tk按钮代替ttk按钮,确保可以设置颜色
        guide_button = tk.Button(
            guide_frame, 
            text="⚠️ 浏览器配置指南(点击查看) ⚠️", 
            command=self.show_browser_guide,
            bg="red",  # 背景色设为红色
            fg="white",  # 文字设为白色
            font=("Arial", 12, "bold"),
            relief=tk.RAISED,  # 凸起的按钮效果
            bd=3,  # 边框宽度
            padx=10,
            pady=5,
            cursor="hand2"  # 鼠标悬停时显示手型指针
        )
        guide_button.pack(fill=tk.X, pady=5)

    def show_browser_guide(self):
        """显示浏览器配置指南弹窗"""
        guide_window = tk.Toplevel(self.root)
        guide_window.title("Chrome浏览器配置指南")
        guide_window.geometry("600x400")
        guide_window.grab_set()  # 模态窗口
        
        # 添加滚动条
        frame = ttk.Frame(guide_window)
        frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        scrollbar = ttk.Scrollbar(frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
        
        # 使用Text控件显示配置指南
        text = tk.Text(frame, wrap=tk.WORD, yscrollcommand=scrollbar.set)
        text.pack(fill=tk.BOTH, expand=True)
        scrollbar.config(command=text.yview)
        
        # 设置文本样式
        text.tag_configure("title", font=("Arial", 14, "bold"), foreground="blue")
        text.tag_configure("subtitle", font=("Arial", 12, "bold"), foreground="dark blue")
        text.tag_configure("normal", font=("Arial", 11))
        text.tag_configure("code", font=("Courier New", 10), background="#f0f0f0")
        text.tag_configure("important", font=("Arial", 11, "bold"), foreground="red")
        
        # 添加配置指南内容
        text.insert(tk.END, "Chrome浏览器配置指南\n\n", "title")
        
        text.insert(tk.END, "步骤 1: 关闭所有Chrome浏览器\n", "subtitle")
        text.insert(tk.END, "在开始之前,请确保关闭所有已经运行的Chrome浏览器窗口。\n\n", "normal")
        
        text.insert(tk.END, "步骤 2: 以调试模式启动Chrome\n", "subtitle")
        text.insert(tk.END, "打开命令提示符(cmd)或PowerShell,输入以下命令:\n", "normal")
        
        cmd = '"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" --remote-debugging-port=9222 --user-data-dir="C:\\ChromeDebug"'
        text.insert(tk.END, f"{cmd}\n\n", "code")
        
        text.insert(tk.END, "注意:请将上面的Chrome路径替换为你电脑上的实际Chrome安装路径。\n", "important")
        text.insert(tk.END, "路径中如果有空格,需要用引号括起来。\n\n", "normal")
        
        text.insert(tk.END, "步骤 3: 打开MidJourney网页\n", "subtitle")
        text.insert(tk.END, "在调试模式启动的Chrome浏览器中:\n", "normal")
        text.insert(tk.END, "1. 登录你的MidJourney账号\n", "normal")
        text.insert(tk.END, "2. 打开MidJourney的web界面\n", "normal")
        text.insert(tk.END, "3. 确保页面上有可输入提示词的输入框\n\n", "normal")
        
        text.insert(tk.END, "步骤 4: 配置本工具\n", "subtitle")
        text.insert(tk.END, "回到本工具,设置以下内容:\n", "normal")
        text.insert(tk.END, "- Chrome路径:选择你的Chrome浏览器可执行文件路径\n", "normal")
        text.insert(tk.END, "- 调试地址:通常保持默认值 127.0.0.1:9222\n", "normal")
        text.insert(tk.END, "- Excel文件:选择包含提示词的Excel文件\n\n", "normal")
        
        text.insert(tk.END, "重要提示\n", "subtitle")
        text.insert(tk.END, "如果连接失败,请检查:\n", "normal")
        text.insert(tk.END, "1. Chrome是否确实以调试模式启动\n", "normal")
        text.insert(tk.END, "2. 调试地址是否正确\n", "normal")
        text.insert(tk.END, "3. 防火墙是否阻止了连接\n", "normal")
        
        # 复制命令按钮
        def copy_command():
            self.root.clipboard_clear()
            self.root.clipboard_append(cmd)
            messagebox.showinfo("成功", "命令已复制到剪贴板")
        
        copy_btn = ttk.Button(guide_window, text="复制启动命令", command=copy_command)
        copy_btn.pack(pady=10)
        
        # 设置文本为只读
        text.config(state=tk.DISABLED)

    def create_settings_frame(self):
        settings_frame = ttk.LabelFrame(self.main_frame, text="设置", padding="10")
        settings_frame.pack(fill=tk.X, pady=5)

        # Chrome路径设置
        chrome_frame = ttk.Frame(settings_frame)
        chrome_frame.pack(fill=tk.X, pady=5)

        ttk.Label(chrome_frame, text="Chrome路径:").pack(side=tk.LEFT)
        self.chrome_path_var = tk.StringVar(value=self.config["chrome_path"])
        chrome_entry = ttk.Entry(chrome_frame, textvariable=self.chrome_path_var, width=50)
        chrome_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)

        chrome_btn = ttk.Button(chrome_frame, text="浏览...", command=self.browse_chrome)
        chrome_btn.pack(side=tk.LEFT)

        # 调试地址设置
        debug_frame = ttk.Frame(settings_frame)
        debug_frame.pack(fill=tk.X, pady=5)

        ttk.Label(debug_frame, text="调试地址:").pack(side=tk.LEFT)
        self.debug_addr_var = tk.StringVar(value=self.config["debugger_address"])
        debug_entry = ttk.Entry(debug_frame, textvariable=self.debug_addr_var)
        debug_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)

        # Excel文件路径设置
        excel_frame = ttk.Frame(settings_frame)
        excel_frame.pack(fill=tk.X, pady=5)

        ttk.Label(excel_frame, text="Excel文件:").pack(side=tk.LEFT)
        self.excel_path_var = tk.StringVar(value=self.config["excel_path"])
        excel_entry = ttk.Entry(excel_frame, textvariable=self.excel_path_var, width=50)
        excel_entry.pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)

        excel_btn = ttk.Button(excel_frame, text="浏览...", command=self.browse_excel)
        excel_btn.pack(side=tk.LEFT)

        # 等待时间设置
        wait_frame = ttk.Frame(settings_frame)
        wait_frame.pack(fill=tk.X, pady=5)

        ttk.Label(wait_frame, text="等待时间范围(秒):").pack(side=tk.LEFT)

        self.min_wait_var = tk.StringVar(value=str(self.config["min_wait"]))
        min_wait_entry = ttk.Entry(wait_frame, textvariable=self.min_wait_var, width=5)
        min_wait_entry.pack(side=tk.LEFT, padx=5)

        ttk.Label(wait_frame, text="").pack(side=tk.LEFT)

        self.max_wait_var = tk.StringVar(value=str(self.config["max_wait"]))
        max_wait_entry = ttk.Entry(wait_frame, textvariable=self.max_wait_var, width=5)
        max_wait_entry.pack(side=tk.LEFT, padx=5)

    def create_log_frame(self):
        log_frame = ttk.LabelFrame(self.main_frame, text="运行日志", padding="10")
        log_frame.pack(fill=tk.BOTH, expand=True, pady=5)

        # 创建日志文本框和滚动条
        self.log_text = tk.Text(log_frame, height=10, wrap=tk.WORD)
        scrollbar = ttk.Scrollbar(log_frame, command=self.log_text.yview)
        self.log_text.configure(yscrollcommand=scrollbar.set)

        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 进度条
        self.progress_frame = ttk.Frame(log_frame)
        self.progress_frame.pack(fill=tk.X, pady=5)

        self.progress_var = tk.DoubleVar()
        self.progress_bar = ttk.Progressbar(self.progress_frame, variable=self.progress_var, maximum=100)
        self.progress_bar.pack(fill=tk.X)

        self.progress_label = ttk.Label(self.progress_frame, text="0/0")
        self.progress_label.pack(pady=2)

    def create_button_frame(self):
        button_frame = ttk.Frame(self.main_frame)
        button_frame.pack(fill=tk.X, pady=10)

        self.start_button = ttk.Button(button_frame, text="开始运行", command=self.start_process)
        self.start_button.pack(side=tk.LEFT, padx=5)

        self.stop_button = ttk.Button(button_frame, text="停止运行", command=self.stop_process, state=tk.DISABLED)
        self.stop_button.pack(side=tk.LEFT, padx=5)

        save_config_btn = ttk.Button(button_frame, text="保存配置", command=self.save_config)
        save_config_btn.pack(side=tk.RIGHT, padx=5)

    def browse_chrome(self):
        file_path = filedialog.askopenfilename(
            title="选择Chrome浏览器可执行文件",
            filetypes=[("可执行文件", "*.exe")]
        )
        if file_path:
            self.chrome_path_var.set(file_path)

    def browse_excel(self):
        file_path = filedialog.askopenfilename(
            title="选择Excel文件",
            filetypes=[("Excel文件", "*.xlsx *.xls")]
        )
        if file_path:
            self.excel_path_var.set(file_path)

    def log(self, message):
        self.log_text.insert(tk.END, f"{message}\n")
        self.log_text.see(tk.END)
        self.root.update_idletasks()

    def update_progress(self, current, total):
        if total > 0:
            self.progress_var.set((current / total) * 100)
            self.progress_label.config(text=f"{current}/{total}")
            self.root.update_idletasks()

    def save_config(self):
        try:
            self.config["chrome_path"] = self.chrome_path_var.get()
            self.config["debugger_address"] = self.debug_addr_var.get()
            self.config["excel_path"] = self.excel_path_var.get()
            self.config["min_wait"] = float(self.min_wait_var.get())
            self.config["max_wait"] = float(self.max_wait_var.get())

            with open("mj_input_config.json", "w", encoding="utf-8") as f:
                json.dump(self.config, f, indent=4, ensure_ascii=False)

            messagebox.showinfo("成功", "配置已保存")
        except Exception as e:
            messagebox.showerror("错误", f"保存配置失败: {str(e)}")

    def load_config(self):
        try:
            if os.path.exists("mj_input_config.json"):
                with open("mj_input_config.json", "r", encoding="utf-8") as f:
                    loaded_config = json.load(f)
                    # 更新配置,保留默认值
                    for key in loaded_config:
                        if key in self.config:
                            self.config[key] = loaded_config[key]
        except Exception as e:
            print(f"加载配置失败: {str(e)}")

    def start_process(self):
        if self.running:
            return

        # 保存当前配置
        self.save_config()

        # 检查Excel文件是否存在
        excel_path = self.excel_path_var.get()
        if not os.path.exists(excel_path):
            messagebox.showerror("错误", f"Excel文件不存在: {excel_path}")
            return

        # 检查Chrome路径是否存在
        chrome_path = self.chrome_path_var.get()
        if not os.path.exists(chrome_path):
            messagebox.showerror("错误", f"Chrome浏览器路径不存在: {chrome_path}")
            return

        # 更新UI状态
        self.start_button.config(state=tk.DISABLED)
        self.stop_button.config(state=tk.NORMAL)
        self.running = True

        # 清空日志
        self.log_text.delete(1.0, tk.END)

        # 启动处理线程
        self.thread = Thread(target=self.process_excel)
        self.thread.daemon = True
        self.thread.start()

    def stop_process(self):
        if not self.running:
            return

        self.running = False
        self.log("正在停止处理...")

        # 更新UI状态
        self.stop_button.config(state=tk.DISABLED)
        self.start_button.config(state=tk.NORMAL)

    def process_excel(self):
        try:
            # 获取配置
            chrome_path = self.chrome_path_var.get()
            debugger_address = self.debug_addr_var.get()
            excel_path = self.excel_path_var.get()
            min_wait = float(self.min_wait_var.get())
            max_wait = float(self.max_wait_var.get())

            self.log(f"开始处理Excel文件: {excel_path}")
            self.log(f"Chrome路径: {chrome_path}")
            self.log(f"调试地址: {debugger_address}")

            # 初始化浏览器
            try:
                options = ChromiumOptions()
                options.binary_location = chrome_path
                options.debugger_address = debugger_address
                page = ChromiumPage(options)
                self.log("浏览器初始化成功")
            except Exception as e:
                self.log(f"浏览器初始化失败: {str(e)}")
                messagebox.showerror("错误", f"浏览器初始化失败: {str(e)}")
                self.stop_process()
                return

            # 等待输入框加载
            try:
                self.log("等待页面输入框加载...")
                page.wait.eles_loaded('#desktop_input_bar', timeout=10)
                self.log("输入框加载成功")
            except Exception as e:
                self.log(f"等待输入框超时: {str(e)}")
                messagebox.showerror("错误", "等待输入框超时,请确保页面已正确加载")
                self.stop_process()
                return

            # 读取Excel文件
            try:
                df = pd.read_excel(excel_path, header=None)
                total_rows = len(df)
                self.log(f"成功读取Excel文件,共{total_rows}行数据")
                self.update_progress(0, total_rows)
            except Exception as e:
                self.log(f"读取Excel文件失败: {str(e)}")
                messagebox.showerror("错误", f"读取Excel文件失败: {str(e)}")
                self.stop_process()
                return

            # 处理每一行
            processed = 0
            for idx, row in df.iterrows():
                if not self.running:
                    self.log("用户停止了处理")
                    break

                # 检查第一列是否为空
                if pd.isna(row[0]):
                    self.log(f"第{idx + 1}行第一列为空,跳过")
                    processed += 1
                    self.update_progress(processed, total_rows)
                    continue

                try:
                    # 获取输入框元素
                    input_element = page.ele('#desktop_input_bar')
                    if input_element:
                        input_element.clear()
                        prompt_text = str(row[0])  # 取第一列的值
                        input_element.input(prompt_text)
                        time.sleep(1)  # 短暂等待输入完成
                        page.actions.key_down('ENTER')

                        self.log(f"已处理第 {idx + 1} 条提示词: {prompt_text}")
                    else:
                        self.log(f"输入框不存在,跳过第 {idx + 1} 条数据")
                except Exception as e:
                    self.log(f"处理第 {idx + 1} 行时出错: {str(e)}")

                # 更新进度
                processed += 1
                self.update_progress(processed, total_rows)

                # 随机等待时间
                wait_time = random.uniform(min_wait, max_wait)
                self.log(f"等待 {wait_time:.2f} 秒...")

                # 分段等待,以便能够响应停止请求
                wait_segments = int(wait_time * 10)  # 每0.1秒检查一次
                for _ in range(wait_segments):
                    if not self.running:
                        break
                    time.sleep(0.1)

            if self.running:
                self.log("所有提示词处理完成!")
                messagebox.showinfo("完成", "所有提示词处理完成!")

        except Exception as e:
            self.log(f"处理过程中发生错误: {str(e)}")
            messagebox.showerror("错误", f"处理过程中发生错误: {str(e)}")
        finally:
            # 恢复UI状态
            self.running = False
            self.root.after(0, lambda: self.start_button.config(state=tk.NORMAL))
            self.root.after(0, lambda: self.stop_button.config(state=tk.DISABLED))


# 主程序入口
if __name__ == "__main__":
    root = tk.Tk()
    app = MJInputApp(root)
    root.mainloop()

 

posted @ 2025-05-28 10:14  *感悟人生*  阅读(36)  评论(0)    收藏  举报