乐于分享
好东西不私藏

AI绘图程序开发

AI绘图程序开发

AI绘图程序开发

首先利用Hermes agent输入要求: 你是一个资深的程序专家,写一段Python程序,主要功能及用途如下:1,采用对话框界面,2,根据API和网址以及选定的模型名称调用模型进行绘图 

展示 软件界面:

 绘制成果: 

第一版代码如下:

#!/usr/bin/env python3# -*- coding: utf-8 -*-"""AI 图像生成工具 (AI Image Generator)=====================================功能:通过 GUI 对话框界面,调用支持 OpenAI 兼容 API 的图像生成模型进行绘图。支持平台:OpenAI DALL-E、Stability AI、硅基流动(SiliconFlow)、通义万相、智谱 CogView 等。依赖安装: pip install tkinter (通常 Python 自带) pip install requests pip install Pillow使用方法: python ai_image_generator.py"""import tkinter as tkfrom tkinter import ttk, messagebox, filedialog, scrolledtextimport requestsimport jsonimport base64import osimport sysimport threadingfrom datetime import datetimefrom pathlib import Pathfrom io import BytesIOtry:from PIL import Image, ImageTkexcept ImportError:print("❌ 缺少 Pillow 库,请执行:pip install Pillow") sys.exit(1)try:import requestsexcept ImportError:print("❌ 缺少 requests 库,请执行:pip install requests") sys.exit(1)# ============================================================# 预设模型配置# ============================================================PRESET_MODELS = {"OpenAI DALL-E 3": {"api_base""https://api.openai.com/v1","model""dall-e-3","endpoint""/images/generations","sizes": ["1024x1024""1024x1792""1792x1024"],"default_size""1024x1024","style_options": ["vivid""natural"],"quality_options": ["standard""hd"],"auth_type""bearer","note""需要 OpenAI API Key,支持 DALL-E 3", },"OpenAI DALL-E 2": {"api_base""https://api.openai.com/v1","model""dall-e-2","endpoint""/images/generations","sizes": ["256x256""512x512""1024x1024"],"default_size""1024x1024","style_options"None,"quality_options"None,"auth_type""bearer","note""需要 OpenAI API Key", },"硅基流动 SiliconFlow": {"api_base""https://api.siliconflow.cn/v1","model""stabilityai/stable-diffusion-3-medium","endpoint""/images/generations","sizes": ["1024x1024""1024x768""768x1024""512x512"],"default_size""1024x1024","style_options"None,"quality_options"None,"auth_type""bearer","note""国内可用,支持多种开源模型", },"智谱 CogView-3": {"api_base""https://open.bigmodel.cn/api/paas/v4","model""cogview-3-plus","endpoint""/images/generations","sizes": ["1024x1024""768x1344""864x1152""1344x768""1152x864""1440x720""720x1440"],"default_size""1024x1024","style_options"None,"quality_options"None,"auth_type""bearer","note""智谱清言出品,国内可用", },"通义万相": {"api_base""https://dashscope.aliyuncs.com/compatible-mode/v1","model""wanx-v1","endpoint""/images/generations","sizes": ["1024*1024""720*1280""1280*720"],"default_size""1024*1024","style_options"None,"quality_options"None,"auth_type""bearer","note""阿里云通义万相,国内可用", },"自定义 OpenAI 兼容": {"api_base""","model""","endpoint""/images/generations","sizes": ["1024x1024""512x512""256x256"],"default_size""1024x1024","style_options"None,"quality_options"None,"auth_type""bearer","note""适用于任何 OpenAI 兼容的图像生成 API", },}# ============================================================# 配置持久化# ============================================================CONFIG_FILE = Path.home() / ".ai_image_generator_config.json"defload_config():"""加载保存的配置"""if CONFIG_FILE.exists():try:withopen(CONFIG_FILE, "r", encoding="utf-8"as f:return json.load(f)except Exception:passreturn {}defsave_config(config):"""保存配置到文件"""try:withopen(CONFIG_FILE, "w", encoding="utf-8"as f: json.dump(config, f, ensure_ascii=False, indent=2)except Exception:pass# ============================================================# 主应用程序# ============================================================classAIImageGenerator:"""AI 图像生成器主窗口"""def__init__(self, root: tk.Tk):self.root = rootself.root.title("🎨 AI 图像生成工具")self.root.geometry("960x800")self.root.minsize(800650)# 状态变量self.generated_images: list[Image.Image] = []self.current_image_index = 0self.is_generating = Falseself.api_key = tk.StringVar()self.api_base = tk.StringVar()self.model_name = tk.StringVar()self.image_size = tk.StringVar(value="1024x1024")self.image_style = tk.StringVar(value="vivid")self.image_quality = tk.StringVar(value="standard")self.n_var = tk.IntVar(value=1)self.prompt_text = tk.StringVar()# 加载配置 saved = load_config()self.api_key.set(saved.get("api_key"""))self.api_base.set(saved.get("api_base"""))self.model_name.set(saved.get("model_name"""))# 默认输出目录self.output_dir = Path.home() / "AI_Generated_Images"self.output_dir.mkdir(exist_ok=True)# 构建界面self._build_styles()self._build_ui()# 默认选择self.combo_preset.current(0)self.combo_preset.event_generate("<<ComboboxSelected>>")# ----------------------------------------------------------# 样式# ----------------------------------------------------------def_build_styles(self): style = ttk.Style()try: style.theme_use("clam")except Exception:pass style.configure("Title.TLabel", font=("Microsoft YaHei UI"14"bold")) style.configure("Section.TLabel", font=("Microsoft YaHei UI"10"bold")) style.configure("TButton", font=("Microsoft YaHei UI"10), padding=6) style.configure("Generate.TButton", font=("Microsoft YaHei UI"11"bold"), padding=8) style.configure("TLabelframe.Label", font=("Microsoft YaHei UI"10"bold")) style.configure("Status.TLabel", font=("Microsoft YaHei UI"9), foreground="gray")# ----------------------------------------------------------# 界面构建# ----------------------------------------------------------def_build_ui(self):# 顶层容器(可滚动) outer = ttk.Frame(self.root, padding=10) outer.pack(fill=tk.BOTH, expand=True)# ---- 标题 ---- ttk.Label(outer, text="🎨 AI 图像生成工具", style="Title.TLabel").pack(anchor=tk.W, pady=(010))# ---- 使用 PanedWindow 上下分割 ---- paned = ttk.PanedWindow(outer, orient=tk.VERTICAL) paned.pack(fill=tk.BOTH, expand=True)# ============ 上半部分:设置区 ============ top_frame = ttk.Frame(paned) paned.add(top_frame, weight=1)# --- 预设选择 --- preset_frame = ttk.LabelFrame(top_frame, text="预设模型", padding=8) preset_frame.pack(fill=tk.X, pady=(06))self.combo_preset = ttk.Combobox( preset_frame, values=list(PRESET_MODELS.keys()), state="readonly", width=35, font=("Microsoft YaHei UI"10), )self.combo_preset.pack(side=tk.LEFT, padx=(010))self.combo_preset.bind("<<ComboboxSelected>>"self._on_preset_changed)self.lbl_note = ttk.Label(preset_frame, text="", style="Status.TLabel", wraplength=500)self.lbl_note.pack(side=tk.LEFT, fill=tk.X, expand=True)# --- API 设置 --- api_frame = ttk.LabelFrame(top_frame, text="API 设置", padding=8) api_frame.pack(fill=tk.X, pady=(06)) ttk.Label(api_frame, text="API 网址:").grid(row=0, column=0, sticky=tk.W, pady=2)self.entry_api_base = ttk.Entry(api_frame, textvariable=self.api_base, width=70, font=("Consolas"10))self.entry_api_base.grid(row=0, column=1, columnspan=2, sticky=tk.EW, padx=5, pady=2) ttk.Label(api_frame, text="API Key:").grid(row=1, column=0, sticky=tk.W, pady=2)self.entry_api_key = ttk.Entry(api_frame, textvariable=self.api_key, width=70, show="●", font=("Consolas"10))self.entry_api_key.grid(row=1, column=1, sticky=tk.EW, padx=5, pady=2)self.btn_show_key = ttk.Button(api_frame, text="👁", width=3, command=self._toggle_key_visibility)self.btn_show_key.grid(row=1, column=2, padx=2, pady=2) ttk.Label(api_frame, text="模型名称:").grid(row=2, column=0, sticky=tk.W, pady=2)self.entry_model = ttk.Entry(api_frame, textvariable=self.model_name, width=70, font=("Consolas"10))self.entry_model.grid(row=2, column=1, columnspan=2, sticky=tk.EW, padx=5, pady=2) api_frame.columnconfigure(1, weight=1)# --- 参数设置 --- param_frame = ttk.LabelFrame(top_frame, text="生成参数", padding=8) param_frame.pack(fill=tk.X, pady=(06))# 第一行:尺寸 ttk.Label(param_frame, text="图片尺寸:").grid(row=0, column=0, sticky=tk.W, pady=2)self.combo_size = ttk.Combobox(param_frame, textvariable=self.image_size, state="readonly", width=15, font=("Consolas"10))self.combo_size.grid(row=0, column=1, padx=5, pady=2)# 数量 ttk.Label(param_frame, text="生成数量:").grid(row=0, column=2, sticky=tk.W, padx=(200), pady=2)self.spin_n = ttk.Spinbox(param_frame, from_=1, to=4, textvariable=self.n_var, width=5, font=("Consolas"10))self.spin_n.grid(row=0, column=3, padx=5, pady=2)# 第二行:风格 & 质量(可能隐藏)self.lbl_style = ttk.Label(param_frame, text="风格:")self.lbl_style.grid(row=1, column=0, sticky=tk.W, pady=2)self.combo_style = ttk.Combobox(param_frame, textvariable=self.image_style, state="readonly", width=15, font=("Consolas"10))self.combo_style.grid(row=1, column=1, padx=5, pady=2)self.lbl_quality = ttk.Label(param_frame, text="质量:")self.lbl_quality.grid(row=1, column=2, sticky=tk.W, padx=(200), pady=2)self.combo_quality = ttk.Combobox(param_frame, textvariable=self.image_quality, state="readonly", width=15, font=("Consolas"10))self.combo_quality.grid(row=1, column=3, padx=5, pady=2)# --- Prompt --- prompt_frame = ttk.LabelFrame(top_frame, text="绘图描述 (Prompt)", padding=8) prompt_frame.pack(fill=tk.BOTH, expand=True, pady=(06))self.txt_prompt = scrolledtext.ScrolledText( prompt_frame, height=4, font=("Microsoft YaHei UI"10), wrap=tk.WORD )self.txt_prompt.pack(fill=tk.BOTH, expand=True)# --- 按钮行 --- btn_frame = ttk.Frame(top_frame) btn_frame.pack(fill=tk.X, pady=(04))self.btn_generate = ttk.Button( btn_frame, text="✨ 开始生成", style="Generate.TButton", command=self._on_generate )self.btn_generate.pack(side=tk.LEFT, padx=(010))self.btn_save = ttk.Button(btn_frame, text="💾 保存图片", command=self._on_save, state=tk.DISABLED)self.btn_save.pack(side=tk.LEFT, padx=(010))self.btn_save_all = ttk.Button(btn_frame, text="📁 保存全部", command=self._on_save_all, state=tk.DISABLED)self.btn_save_all.pack(side=tk.LEFT, padx=(010))self.btn_prev = ttk.Button(btn_frame, text="◀", width=3, command=self._on_prev_image, state=tk.DISABLED)self.btn_prev.pack(side=tk.RIGHT, padx=2)self.btn_next = ttk.Button(btn_frame, text="▶", width=3, command=self._on_next_image, state=tk.DISABLED)self.btn_next.pack(side=tk.RIGHT, padx=2)self.lbl_nav = ttk.Label(btn_frame, text="", style="Status.TLabel")self.lbl_nav.pack(side=tk.RIGHT, padx=10)# ============ 下半部分:图片预览 & 日志 ============ bottom_frame = ttk.Frame(paned) paned.add(bottom_frame, weight=1)# 预览 preview_frame = ttk.LabelFrame(bottom_frame, text="图片预览", padding=5) preview_frame.pack(fill=tk.BOTH, expand=True, pady=(04))self.canvas = tk.Canvas(preview_frame, bg="#f0f0f0", highlightthickness=0)self.canvas.pack(fill=tk.BOTH, expand=True)# 状态栏self.status_var = tk.StringVar(value="就绪 — 选择预设模型或填写 API 信息后开始生成")self.lbl_status = ttk.Label(bottom_frame, textvariable=self.status_var, style="Status.TLabel", anchor=tk.W)self.lbl_status.pack(fill=tk.X)# 绑定关闭事件self.root.protocol("WM_DELETE_WINDOW"self._on_close)self.root.bind("<Control-s>"lambda e: self._on_save())self.root.bind("<Control-Return>"lambda e: self._on_generate())# ----------------------------------------------------------# 预设切换# ----------------------------------------------------------def_on_preset_changed(self, event=None): name = self.combo_preset.get()if name notin PRESET_MODELS:return cfg = PRESET_MODELS[name]self.api_base.set(cfg["api_base"])self.model_name.set(cfg["model"])self.lbl_note.config(text=cfg.get("note"""))# 尺寸self.combo_size["values"] = cfg["sizes"]self.image_size.set(cfg["default_size"])# 风格if cfg.get("style_options"):self.combo_style["values"] = cfg["style_options"]self.image_style.set(cfg["style_options"][^0])self.lbl_style.grid()self.combo_style.grid()else:self.lbl_style.grid_remove()self.combo_style.grid_remove()# 质量if cfg.get("quality_options"):self.combo_quality["values"] = cfg["quality_options"]self.image_quality.set(cfg["quality_options"][^0])self.lbl_quality.grid()self.combo_quality.grid()else:self.lbl_quality.grid_remove()self.combo_quality.grid_remove()# ----------------------------------------------------------# 辅助# ----------------------------------------------------------def_toggle_key_visibility(self):ifself.entry_api_key["show"] == "●":self.entry_api_key.config(show="")self.btn_show_key.config(text="🔒")else:self.entry_api_key.config(show="●")self.btn_show_key.config(text="👁")def_set_status(self, text):self.status_var.set(text)self.root.update_idletasks()def_update_nav(self): n = len(self.generated_images)if n == 0:self.lbl_nav.config(text="")self.btn_prev.config(state=tk.DISABLED)self.btn_next.config(state=tk.DISABLED)self.btn_save.config(state=tk.DISABLED)self.btn_save_all.config(state=tk.DISABLED)elif n == 1:self.lbl_nav.config(text="1 / 1")self.btn_prev.config(state=tk.DISABLED)self.btn_next.config(state=tk.DISABLED)self.btn_save.config(state=tk.NORMAL)self.btn_save_all.config(state=tk.DISABLED)else: idx = self.current_image_index + 1self.lbl_nav.config(text=f"{idx} / {n}")self.btn_prev.config(state=tk.NORMAL ifself.current_image_index > 0else tk.DISABLED)self.btn_next.config(state=tk.NORMAL ifself.current_image_index < n - 1else tk.DISABLED)self.btn_save.config(state=tk.NORMAL)self.btn_save_all.config(state=tk.NORMAL)def_display_image(self, img: Image.Image):"""在 Canvas 上居中显示图片"""self.canvas.delete("all") cw = self.canvas.winfo_width() ch = self.canvas.winfo_height()if cw < 10or ch < 10: cw, ch = 800300# 等比缩放 img_w, img_h = img.size scale = min(cw / img_w, ch / img_h, 1.0) new_w = int(img_w * scale) new_h = int(img_h * scale) resized = img.resize((new_w, new_h), Image.LANCZOS)self._tk_photo = ImageTk.PhotoImage(resized) x = (cw - new_w) // 2 y = (ch - new_h) // 2self.canvas.create_image(x, y, anchor=tk.NW, image=self._tk_photo)# ----------------------------------------------------------# 生成逻辑# ----------------------------------------------------------def_on_generate(self):ifself.is_generating:return# 校验 api_base = self.api_base.get().strip() api_key = self.api_key.get().strip() model = self.model_name.get().strip() prompt = self.txt_prompt.get("1.0", tk.END).strip()ifnot api_base: messagebox.showwarning("提示""请填写 API 网址")returnifnot api_key: messagebox.showwarning("提示""请填写 API Key")returnifnot model: messagebox.showwarning("提示""请填写模型名称")returnifnot prompt: messagebox.showwarning("提示""请输入绘图描述")return# 禁用按钮self.is_generating = Trueself.btn_generate.config(state=tk.DISABLED, text="⏳ 生成中...")self._set_status("正在生成图片,请稍候...")# 获取预设配置 preset_name = self.combo_preset.get() preset_cfg = PRESET_MODELS.get(preset_name, {})# 在后台线程中执行请求 thread = threading.Thread( target=self._generate_thread, args=(api_base, api_key, model, prompt, preset_cfg), daemon=True, ) thread.start()def_generate_thread(self, api_base, api_key, model, prompt, preset_cfg):"""后台线程:调用 API 并在画布上显示结果"""try: images = self._call_api(api_base, api_key, model, prompt, preset_cfg)if images:self.generated_images = imagesself.current_image_index = 0self.root.after(0self._show_result, images)else:self.root.after(0self._show_error, "未生成任何图片,请检查参数或 API 响应。")except Exception as e:self.root.after(0self._show_error, str(e))finally:self.is_generating = Falseself.root.after(0self._reset_generate_button)def_call_api(self, api_base, api_key, model, prompt, preset_cfg) -> list:"""调用 OpenAI 兼容的图像生成 API""" url = api_base.rstrip("/") + "/images/generations" headers = {"Content-Type""application/json","Authorization"f"Bearer {api_key}", } body: dict = {"model": model,"prompt": prompt,"n"self.n_var.get(),"size"self.image_size.get().replace("*""x"), }# 可选参数if preset_cfg.get("style_options"): body["style"] = self.image_style.get()if preset_cfg.get("quality_options"): body["quality"] = self.image_quality.get()# response_format 优先 base64,避免 URL 过期问题 body["response_format"] = "b64_json" resp = requests.post(url, headers=headers, json=body, timeout=120) resp.raise_for_status() data = resp.json()# 解析响应 images: list[Image.Image] = []for item in data.get("data", []):if"b64_json"in item: img_data = base64.b64decode(item["b64_json"]) img = Image.open(BytesIO(img_data)).convert("RGBA") images.append(img)elif"url"in item: img_resp = requests.get(item["url"], timeout=60) img_resp.raise_for_status() img = Image.open(BytesIO(img_resp.content)).convert("RGBA") images.append(img)elif"image"in item:# 某些 API 直接返回 base64 字段 img_data = base64.b64decode(item["image"]) img = Image.open(BytesIO(img_data)).convert("RGBA") images.append(img)return imagesdef_show_result(self, images):self._display_image(images[^0])self._update_nav()self._set_status(f"✅ 成功生成 {len(images)} 张图片")def_show_error(self, msg): messagebox.showerror("生成失败", msg)self._set_status(f"❌ 生成失败:{msg}")def_reset_generate_button(self):self.btn_generate.config(state=tk.NORMAL, text="✨ 开始生成")# ----------------------------------------------------------# 图片导航# ----------------------------------------------------------def_on_prev_image(self):ifself.current_image_index > 0:self.current_image_index -= 1self._display_image(self.generated_images[self.current_image_index])self._update_nav()def_on_next_image(self):ifself.current_image_index < len(self.generated_images) - 1:self.current_image_index += 1self._display_image(self.generated_images[self.current_image_index])self._update_nav()# ----------------------------------------------------------# 保存# ----------------------------------------------------------def_on_save(self):ifnotself.generated_images:return img = self.generated_images[self.current_image_index]self._save_single_image(img, self.current_image_index)def_on_save_all(self):ifnotself.generated_images:return# 弹出选择目录 folder = filedialog.askdirectory(title="选择保存目录", initialdir=str(self.output_dir))ifnot folder:return saved = []for i, img inenumerate(self.generated_images): ts = datetime.now().strftime("%Y%m%d_%H%M%S") filename = f"ai_image_{ts}_{i + 1}.png" filepath = Path(folder) / filename img.save(str(filepath), "PNG") saved.append(str(filepath))self._set_status(f"✅ 已保存 {len(saved)} 张图片到 {folder}") messagebox.showinfo("保存成功"f"已保存 {len(saved)} 张图片到:\n{folder}")def_save_single_image(self, img, index): filepath = filedialog.asksaveasfilename( title="保存图片", initialdir=str(self.output_dir), initialfile=f"ai_image_{index + 1}.png", defaultextension=".png", filetypes=[ ("PNG 图片""*.png"), ("JPEG 图片""*.jpg"), ("WebP 图片""*.webp"), ("所有文件""*.*"), ], )ifnot filepath:return fmt = Path(filepath).suffix.lstrip(".").upper()if fmt == "JPG": fmt = "JPEG" img = img.convert("RGB") img.save(filepath, fmt)self._set_status(f"✅ 已保存: {filepath}")# ----------------------------------------------------------# 关闭# ----------------------------------------------------------def_on_close(self):# 保存配置(不含 API Key) config = {"api_base"self.api_base.get().strip(),"model_name"self.model_name.get().strip(),"api_key"self.api_key.get().strip(),"last_preset"self.combo_preset.get(), } save_config(config)self.root.destroy()# ============================================================# 入口# ============================================================defmain(): root = tk.Tk()# Windows DPI 适配try:from ctypes import windll windll.shcore.SetProcessDpiAwareness(1)except Exception:pass app = AIImageGenerator(root) root.mainloop()if __name__ == "__main__": main() 分析该程序中存在的问题

再通过cherrystudio,利用glm-5.1进行代码审查,虽然先前代码也是Hermes agent通过glm-5.1进行撰写。但是审查中也有很多问题。 这份 Python 代码整体结构清晰,功能完整,使用了多线程避免 GUI 卡顿,并且考虑了 API 兼容性和图片防垃圾回收(self._tk_photo)等细节,是一份不错的代码。经过仔细审查,程序中仍存在一些安全隐患、逻辑漏洞、用户体验缺陷以及冗余代码。以下是详细的问题分析和修改建议:

一、 安全隐患(严重)

1. API Key 明文保存到本地文件 在 _on_close 方法中,程序将 api_key 直接写入了 ~/.ai_image_generator_config.json。这会导致你的 API Key 以明文形式存储在硬盘中,任何能访问该文件的软件或用户都能窃取你的密钥,造成财产损失。

  • 修改建议
  • 方案 A:不保存 API Key,每次启动手动输入(最安全)。
  • 方案 B:使用系统的 Keyring(钥匙串)保存,需要引入 keyring 库。
  • 方案 C:如果一定要用 JSON 保存,至少进行简单的混淆或加密,并在 UI 上增加“记住 Key”的勾选框,让用户自己承担风险。

二、 逻辑漏洞与 Bug(中等)

1. ScrolledText 不支持 textvariable 在 _build_ui 中,你定义了 self.prompt_text = tk.StringVar(),并在 ScrolledText 中尝试使用(虽然代码里你其实没写 textvariable=self.prompt_text,但声明了这个变量)。而在 _on_generate 中,你正确使用了 self.txt_prompt.get("1.0", tk.END)。这说明 self.prompt_text 是死代码,Tkinter 的 Text 控件不支持 textvariable,只有 Entry 支持。

  • 修改建议:删除 self.prompt_text = tk.StringVar()

2. DALL-E 3 不支持生成数量 n > 1 在参数设置中,self.spin_n 允许设置 1-4。但是 OpenAI 的 DALL-E 3 模型严格限制 n 只能为 1。如果用户选择 DALL-E 3 并设置 n=2,API 会报错。

  • 修改建议:在 PRESET_MODELS 中增加 max_n 字段,在 _on_preset_changed 中动态更新 Spinbox 的上限。

3. API 报错信息不清晰 在 _call_api 中使用了 resp.raise_for_status()。如果 API 返回 400/401/429 等错误,这行代码只会抛出一个笼统的 requests.exceptions.HTTPError,丢失了 API 返回的具体错误原因(如“余额不足”、“内容违规”),用户看到的只是一头雾水的报错。

4. 窗口关闭时的多线程竞态条件 如果用户在图片生成过程中(后台线程正在运行)直接关闭窗口,后台线程执行完 self._call_api 后,会调用 self.root.after(0, self._show_result, images)。但此时 root 已经被销毁,会导致 TclError 崩溃。

三、 用户体验缺陷(轻微)

1. 窗口缩放时,图片预览不会自适应_display_image 方法在初次显示图片时能正常缩放,但当你拖动窗口边缘改变大小时,Canvas 中的图片不会自动重新缩放,导致图片变形或留白。

2. 误导性的依赖安装注释 在文件开头的注释中写了 pip install tkinter。这是错误的,tkinter 是 Python 的标准库,通常随 Python 一起安装,无法通过 pip 安装(Linux 下需要 sudo apt install python3-tk)。这会误导新手。

3. 通义万相的尺寸分隔符问题 通义万相的 sizes 定义为 ["1024*1024", ...],而在 _call_api 中你用 self.image_size.get().replace("*", "x") 统一转成了 x。虽然通义万相的 OpenAI 兼容接口可能现在支持 x,但 UI 上显示 1024*1024 而 API 发送 1024x1024 容易让调试的用户产生困惑。建议在配置中统一使用 x

4. 生成按钮防抖逻辑不严谨 在 _on_generate 中,如果 self.is_generating 为 True,直接 return 了,用户没有任何提示。如果网络很慢,用户可能会疯狂点击。

  • 修改建议:生成中时,给用户一个状态反馈,或者让按钮变成不可用状态(其实你在后台线程里做了 self.btn_generate.config(state=tk.DISABLED),但在主线程校验前没做,存在快速双击的可能)。

四、 代码冗余与规范

1. 重复导入 requests 代码第 11 行和第 20 行两次尝试 import requests

  • 修改建议:删除第 11 行的 import requests,只保留下方的带 try...except 的导入。

2. PRESET_MODELS 中的 endpoint 字段未被使用 配置中写了 "endpoint": "/images/generations",但在 _call_api 中你是硬编码拼接的:url = api_base.rstrip("/") + "/images/generations"

最后测试过程中发现存在的问题如下:

1、原始的对话框小了,部分按钮无法完全显示;2、高分屏的自适应问题; **3、输入网址和api时,增加刷新按钮,实现生图模型的列表,在列表中选择模型用于本次生图。

针对你的需求,我进行了全面升级。以下是主要修改说明:

  1. API Key 自动保存:恢复了 API Key 的本地保存功能,下次启动程序时会自动填入上次的 Key。
  2. 新增高级参数区:增加了“高级参数”面板,包含你要求的 5 项控制:
  • 随机种子数 (Seed):留空则随机,填入数字可复现图像。
  • 推理步数 (Steps):1-50,默认 20。
  • 引导比例 (CFG):0-20,默认 7.5。
  • 反向提示词 (Negative Prompt):描述不想出现的内容。
  • 提示词增强 (Prompt Enhancement):勾选后开启。
  1. 智能参数过滤:由于 OpenAI DALL-E 系列不支持步数、引导比例、反向提示词等 SD/Flux 特有参数,如果强行发送会报错。代码中增加了智能判断:当选择 DALL-E 时,自动屏蔽这些参数的发送,确保 API 调用不会报错;当选择硅基流动等模型时,则完整发送。

最后存在一个关键性问题,选定某个AI供应商后,输入api,其他模型也随之采用该api,合理的应该是每个模型供应商,api单独存放一个位置,其他模型供应商的仍为空。 随后对代码进行了重构,实现了 API 配置按供应商独立隔离存储

  1. 独立存储:每个预设模型(如硅基流动、智谱、DALL-E)现在都有自己独立的 api_base 和 api_key 存储空间。
  2. 自动切换:当你选择“硅基流动”时填入其 Key,切换到“智谱”时界面会自动清空并显示智谱的配置;再切回“硅基流动”时,之前填的 Key 会自动恢复。
  3. 自动保存:在切换预设或关闭程序时,当前供应商的配置会自动保存到本地,下次打开无需重填。

最终的完整代码:

#!/usr/bin/env python3# -*- coding: utf-8 -*-"""AI 图像生成工具 + 模型列表获取=====================================功能:通过 GUI 对话框界面,调用支持 OpenAI 兼容 API 的图像生成模型进行绘图。支持平台:OpenAI DALL-E、Stability AI、硅基流动、通义万相、智谱 CogView 等。依赖安装:    pip install requests    pip install Pillow使用方法:    python ai_image_generator.py"""import tkinter as tkfrom tkinter import ttk, messagebox, filedialog, scrolledtextimport requestsimport jsonimport base64import osimport sysimport threadingfrom datetime import datetimefrom pathlib import Pathfrom io import BytesIOtry:from PIL import Image, ImageTkexcept ImportError:print("❌ 缺少 Pillow 库,请执行:pip install Pillow")    sys.exit(1)try:import requestsexcept ImportError:print("❌ 缺少 requests 库,请执行:pip install requests")    sys.exit(1)# ============================================================# 资源路径兼容函数 (支持 PyInstaller 打包)# ============================================================defresource_path(relative_path):"""获取资源绝对路径(兼容开发环境和 PyInstaller 打包后)"""ifhasattr(sys, '_MEIPASS'):        base_path = sys._MEIPASSelse:        base_path = os.path.abspath(".")return os.path.join(base_path, relative_path)# ============================================================# 预设模型配置# ============================================================PRESET_MODELS = {"OpenAI DALL-E 3": {"api_base""https://api.openai.com/v1","model""dall-e-3","sizes": ["1024x1024""1024x1792""1792x1024"],"default_size""1024x1024","style_options": ["vivid""natural"],"quality_options": ["standard""hd"],"note""需要 OpenAI API Key,支持 DALL-E 3 (仅支持单张生成)",    },"OpenAI DALL-E 2": {"api_base""https://api.openai.com/v1","model""dall-e-2","sizes": ["256x256""512x512""1024x1024"],"default_size""1024x1024","style_options"None,"quality_options"None,"note""需要 OpenAI API Key",    },"硅基流动 SiliconFlow": {"api_base""https://api.siliconflow.cn/v1","model""stabilityai/stable-diffusion-3-medium","sizes": ["1024x1024""1024x768""768x1024""512x512"],"default_size""1024x1024","style_options"None,"quality_options"None,"note""国内可用,支持多种开源模型,完美支持高级参数",    },"智谱 CogView-3": {"api_base""https://open.bigmodel.cn/api/paas/v4","model""cogview-3-plus","sizes": ["1024x1024""768x1344""864x1152""1344x768""1152x864""1440x720""720x1440"],"default_size""1024x1024","style_options"None,"quality_options"None,"note""智谱清言出品,国内可用",    },"通义万相": {"api_base""https://dashscope.aliyuncs.com/compatible-mode/v1","model""wanx-v1","sizes": ["1024x1024""720x1280""1280x720"],"default_size""1024x1024","style_options"None,"quality_options"None,"note""阿里云通义万相,国内可用",    },"自定义 OpenAI 兼容": {"api_base""","model""","sizes": ["1024x1024""512x512""256x256"],"default_size""1024x1024","style_options"None,"quality_options"None,"note""适用于任何 OpenAI 兼容的图像生成 API",    },}# ============================================================# 配置持久化# ============================================================CONFIG_FILE = Path.home() / ".ai_image_generator_config.json"defload_config():if CONFIG_FILE.exists():try:withopen(CONFIG_FILE, "r", encoding="utf-8"as f:return json.load(f)except Exception:passreturn {}defsave_config(config):try:withopen(CONFIG_FILE, "w", encoding="utf-8"as f:            json.dump(config, f, ensure_ascii=False, indent=2)except Exception:pass# ============================================================# 主应用程序# ============================================================classAIImageGenerator:def__init__(self, root: tk.Tk):self.root = rootself.root.title("🎨 AI 图像生成工具")self.root.geometry("1100x1200")self.root.minsize(900900)# 状态变量self.generated_images = []self.current_image_index = 0self.is_generating = Falseself.is_fetching_models = Falseself.api_key = tk.StringVar()self.api_base = tk.StringVar()self.model_name = tk.StringVar()self.image_size = tk.StringVar(value="1024x1024")self.image_style = tk.StringVar(value="vivid")self.image_quality = tk.StringVar(value="standard")self.n_var = tk.IntVar(value=1)# 高级参数变量self.seed_var = tk.StringVar(value=""# 留空表示随机self.steps_var = tk.IntVar(value=20)self.guidance_var = tk.DoubleVar(value=7.5)self.negative_prompt_var = tk.StringVar(value="")self.enhance_var = tk.BooleanVar(value=False)# 加载配置 (新逻辑:按供应商独立加载)self.config_data = load_config()self.presets_config = self.config_data.get("presets_config", {})self._current_preset_name = None# 记录当前所处的预设名称self.output_dir = Path.home() / "AI_Generated_Images"self.output_dir.mkdir(exist_ok=True)self._build_styles()self._build_ui()# 触发初始预设加载        last_preset = self.config_data.get("last_preset""OpenAI DALL-E 3")if last_preset in PRESET_MODELS:self.combo_preset.set(last_preset)else:self.combo_preset.current(0)self._on_preset_changed() # 手动触发第一次加载def_build_styles(self):        style = ttk.Style()try:            style.theme_use("clam")except Exception:pass        style.configure("Title.TLabel", font=("Microsoft YaHei UI"14"bold"))        style.configure("Section.TLabel", font=("Microsoft YaHei UI"10"bold"))        style.configure("TButton", font=("Microsoft YaHei UI"10), padding=6)        style.configure("Generate.TButton", font=("Microsoft YaHei UI"11"bold"), padding=8)        style.configure("TLabelframe.Label", font=("Microsoft YaHei UI"10"bold"))        style.configure("Status.TLabel", font=("Microsoft YaHei UI"9), foreground="gray")def_build_ui(self):        outer = ttk.Frame(self.root, padding=10)        outer.pack(fill=tk.BOTH, expand=True)        ttk.Label(outer, text="🎨 AI 图像生成工具", style="Title.TLabel").pack(anchor=tk.W, pady=(010))# ============ 上半部分:设置区 ============        top_frame = ttk.Frame(outer)        top_frame.pack(fill=tk.X, pady=(06))# --- 预设选择 ---        preset_frame = ttk.LabelFrame(top_frame, text="预设模型", padding=8)        preset_frame.pack(fill=tk.X, pady=(06))self.combo_preset = ttk.Combobox(preset_frame, values=list(PRESET_MODELS.keys()), state="readonly", width=35, font=("Microsoft YaHei UI"10))self.combo_preset.pack(side=tk.LEFT, padx=(010))self.combo_preset.bind("<<ComboboxSelected>>"self._on_preset_changed)self.lbl_note = ttk.Label(preset_frame, text="", style="Status.TLabel", wraplength=500)self.lbl_note.pack(side=tk.LEFT, fill=tk.X, expand=True)# --- API 设置 ---        api_frame = ttk.LabelFrame(top_frame, text="API 设置", padding=8)        api_frame.pack(fill=tk.X, pady=(06))        ttk.Label(api_frame, text="API 网址:").grid(row=0, column=0, sticky=tk.W, pady=4)self.entry_api_base = ttk.Entry(api_frame, textvariable=self.api_base, width=65, font=("Consolas"10))self.entry_api_base.grid(row=0, column=1, sticky=tk.EW, padx=5, pady=4)        ttk.Label(api_frame, text="API Key:").grid(row=1, column=0, sticky=tk.W, pady=4)self.entry_api_key = ttk.Entry(api_frame, textvariable=self.api_key, width=65, show="●", font=("Consolas"10))self.entry_api_key.grid(row=1, column=1, sticky=tk.EW, padx=5, pady=4)self.btn_show_key = ttk.Button(api_frame, text="👁", width=3, command=self._toggle_key_visibility)self.btn_show_key.grid(row=1, column=2, padx=2, pady=4)        ttk.Label(api_frame, text="模型名称:").grid(row=2, column=0, sticky=tk.W, pady=4)        model_input_frame = ttk.Frame(api_frame)        model_input_frame.grid(row=2, column=1, columnspan=2, sticky=tk.EW, padx=5, pady=4)self.combo_model = ttk.Combobox(model_input_frame, textvariable=self.model_name, width=50, font=("Consolas"10))self.combo_model.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(05))self.btn_refresh_models = ttk.Button(model_input_frame, text="🔄 刷新模型", command=self._on_refresh_models)self.btn_refresh_models.pack(side=tk.RIGHT)        api_frame.columnconfigure(1, weight=1)# --- 基础参数设置 ---        param_frame = ttk.LabelFrame(top_frame, text="基础参数", padding=8)        param_frame.pack(fill=tk.X, pady=(06))        ttk.Label(param_frame, text="图片尺寸:").grid(row=0, column=0, sticky=tk.W, pady=2)self.combo_size = ttk.Combobox(param_frame, textvariable=self.image_size, state="readonly", width=15, font=("Consolas"10))self.combo_size.grid(row=0, column=1, padx=5, pady=2)        ttk.Label(param_frame, text="生成数量:").grid(row=0, column=2, sticky=tk.W, padx=(200), pady=2)self.spin_n = ttk.Spinbox(param_frame, from_=1, to=4, textvariable=self.n_var, width=5, font=("Consolas"10))self.spin_n.grid(row=0, column=3, padx=5, pady=2)self.lbl_style = ttk.Label(param_frame, text="风格:")self.lbl_style.grid(row=1, column=0, sticky=tk.W, pady=2)self.combo_style = ttk.Combobox(param_frame, textvariable=self.image_style, state="readonly", width=15, font=("Consolas"10))self.combo_style.grid(row=1, column=1, padx=5, pady=2)self.lbl_quality = ttk.Label(param_frame, text="质量:")self.lbl_quality.grid(row=1, column=2, sticky=tk.W, padx=(200), pady=2)self.combo_quality = ttk.Combobox(param_frame, textvariable=self.image_quality, state="readonly", width=15, font=("Consolas"10))self.combo_quality.grid(row=1, column=3, padx=5, pady=2)# --- 高级参数设置 (新增) ---        adv_frame = ttk.LabelFrame(top_frame, text="高级参数 (适用于SD/Flux类模型,DALL-E将自动忽略)", padding=8)        adv_frame.pack(fill=tk.X, pady=(06))# 第一行:种子、步数、引导比例        ttk.Label(adv_frame, text="随机种子:").grid(row=0, column=0, sticky=tk.W, pady=2)self.entry_seed = ttk.Entry(adv_frame, textvariable=self.seed_var, width=15, font=("Consolas"10))self.entry_seed.grid(row=0, column=1, padx=5, pady=2)        ttk.Label(adv_frame, text="(留空随机)", style="Status.TLabel").grid(row=0, column=2, sticky=tk.W, pady=2)        ttk.Label(adv_frame, text="推理步数:").grid(row=0, column=3, sticky=tk.W, padx=(200), pady=2)self.spin_steps = ttk.Spinbox(adv_frame, from_=1, to=50, textvariable=self.steps_var, width=5, font=("Consolas"10))self.spin_steps.grid(row=0, column=4, padx=5, pady=2)        ttk.Label(adv_frame, text="引导比例:").grid(row=0, column=5, sticky=tk.W, padx=(200), pady=2)self.spin_guidance = ttk.Spinbox(adv_frame, from_=0, to=20, increment=0.5, textvariable=self.guidance_var, width=5, font=("Consolas"10))self.spin_guidance.grid(row=0, column=6, padx=5, pady=2)# 第二行:反向提示词        ttk.Label(adv_frame, text="反向提示词:").grid(row=1, column=0, sticky=tk.W, pady=2)self.entry_neg_prompt = ttk.Entry(adv_frame, textvariable=self.negative_prompt_var, width=60, font=("Microsoft YaHei UI"10))self.entry_neg_prompt.grid(row=1, column=1, columnspan=5, sticky=tk.EW, padx=5, pady=2)# 第三行:提示词增强开关self.chk_enhance = ttk.Checkbutton(adv_frame, text="开启提示词增强 (自动重写为详细提示词)", variable=self.enhance_var)self.chk_enhance.grid(row=2, column=0, columnspan=7, sticky=tk.W, pady=2)        adv_frame.columnconfigure(1, weight=1)        adv_frame.columnconfigure(5, weight=1)# --- Prompt ---        prompt_frame = ttk.LabelFrame(top_frame, text="绘图描述", padding=8)        prompt_frame.pack(fill=tk.X, pady=(06))self.txt_prompt = scrolledtext.ScrolledText(prompt_frame, height=3, font=("Microsoft YaHei UI"10), wrap=tk.WORD)self.txt_prompt.pack(fill=tk.X, expand=True)# ============ 固定在中间的操作按钮区 ============        btn_frame = ttk.Frame(outer)        btn_frame.pack(fill=tk.X, pady=8)self.btn_generate = ttk.Button(btn_frame, text="✨ 开始生成", style="Generate.TButton", command=self._on_generate)self.btn_generate.pack(side=tk.LEFT, padx=(010))self.btn_save = ttk.Button(btn_frame, text="💾 保存图片", command=self._on_save, state=tk.DISABLED)self.btn_save.pack(side=tk.LEFT, padx=(010))self.btn_save_all = ttk.Button(btn_frame, text="📁 保存全部", command=self._on_save_all, state=tk.DISABLED)self.btn_save_all.pack(side=tk.LEFT, padx=(010))self.btn_prev = ttk.Button(btn_frame, text="◀ 上一张", width=8, command=self._on_prev_image, state=tk.DISABLED)self.btn_prev.pack(side=tk.RIGHT, padx=2)self.btn_next = ttk.Button(btn_frame, text="下一张 ▶", width=8, command=self._on_next_image, state=tk.DISABLED)self.btn_next.pack(side=tk.RIGHT, padx=2)self.lbl_nav = ttk.Label(btn_frame, text="", style="Status.TLabel")self.lbl_nav.pack(side=tk.RIGHT, padx=10)# ============ 下半部分:图片预览 ============        preview_frame = ttk.LabelFrame(outer, text="图片预览", padding=5)        preview_frame.pack(fill=tk.BOTH, expand=True, pady=(04))self.canvas = tk.Canvas(preview_frame, bg="#f0f0f0", highlightthickness=0)self.canvas.pack(fill=tk.BOTH, expand=True)self.canvas.bind("<Configure>"self._on_canvas_resize)# 状态栏self.status_var = tk.StringVar(value="就绪 — 选择预设模型或填写 API 信息后开始生成")self.lbl_status = ttk.Label(outer, textvariable=self.status_var, style="Status.TLabel", anchor=tk.W)self.lbl_status.pack(fill=tk.X)self.root.protocol("WM_DELETE_WINDOW"self._on_close)self.root.bind("<Control-s>"lambda e: self._on_save())self.root.bind("<Control-Return>"lambda e: self._on_generate())# ----------------------------------------------------------# 预设切换与独立配置保存逻辑# ----------------------------------------------------------def_save_current_preset_config(self, preset_name):"""将当前界面上的 API 配置保存到指定的预设名下"""ifnot preset_name or preset_name notin PRESET_MODELS:returnif preset_name notinself.presets_config:self.presets_config[preset_name] = {}# 保存当前输入框的值self.presets_config[preset_name]["api_base"] = self.api_base.get().strip()self.presets_config[preset_name]["api_key"] = self.api_key.get().strip()def_on_preset_changed(self, event=None):        new_name = self.combo_preset.get()# 1. 保存之前预设的 API 信息ifself._current_preset_name:self._save_current_preset_config(self._current_preset_name)# 2. 更新当前预设名称记录self._current_preset_name = new_nameif new_name notin PRESET_MODELS:return        cfg = PRESET_MODELS[new_name]# 3. 加载新预设的 API 信息 (如果之前没保存过,则使用预设默认值,Key为空)        saved_preset_cfg = self.presets_config.get(new_name, {})self.api_base.set(saved_preset_cfg.get("api_base", cfg.get("api_base""")))self.api_key.set(saved_preset_cfg.get("api_key"""))# 4. 其他原有逻辑 (重置模型、尺寸等)self.model_name.set(cfg["model"])self.lbl_note.config(text=cfg.get("note"""))self.combo_size["values"] = cfg["sizes"]self.image_size.set(cfg["default_size"])if cfg.get("style_options"):self.combo_style["values"] = cfg["style_options"]self.image_style.set(cfg["style_options"][0])self.lbl_style.grid()self.combo_style.grid()else:self.lbl_style.grid_remove()self.combo_style.grid_remove()if cfg.get("quality_options"):self.combo_quality["values"] = cfg["quality_options"]self.image_quality.set(cfg["quality_options"][0])self.lbl_quality.grid()self.combo_quality.grid()else:self.lbl_quality.grid_remove()self.combo_quality.grid_remove()if"DALL-E 3"in new_name:self.spin_n.config(to=1)self.n_var.set(1)else:self.spin_n.config(to=4)# ----------------------------------------------------------# 模型列表刷新逻辑# ----------------------------------------------------------def_on_refresh_models(self):ifself.is_fetching_models:return        api_base = self.api_base.get().strip()        api_key = self.api_key.get().strip()ifnot api_base:            messagebox.showwarning("提示""请先填写 API 网址")returnifnot api_key:            messagebox.showwarning("提示""请先填写 API Key")returnself.is_fetching_models = Trueself.btn_refresh_models.config(state=tk.DISABLED, text="⏳ 获取中...")self._set_status("正在从 API 获取模型列表...")        thread = threading.Thread(            target=self._fetch_models_thread,            args=(api_base, api_key),            daemon=True        )        thread.start()def_fetch_models_thread(self, api_base, api_key):try:            url = api_base.rstrip("/") + "/models"            headers = {"Authorization"f"Bearer {api_key}"}            resp = requests.get(url, headers=headers, timeout=15)if resp.status_code != 200:try:                    err_data = resp.json()                    msg = err_data.get("error", {}).get("message"str(err_data))except:                    msg = resp.textraise Exception(f"HTTP {resp.status_code} - {msg}")            data = resp.json()            all_models = [m.get("id"for m in data.get("data", []) if m.get("id")]ifnot all_models:raise Exception("API 返回的模型列表为空")            image_keywords = ["dall""stable-diffusion""sd""cogview""wanx""flux""midjourney""paint""image""visual"]            filtered_models = [m for m in all_models ifany(kw in m.lower() for kw in image_keywords)]            final_models = filtered_models if filtered_models else all_modelsifself.root.winfo_exists():self.root.after(0self._update_model_list, final_models)except Exception as e:ifself.root.winfo_exists():self.root.after(0self._show_error, f"获取模型列表失败: {e}")finally:self.is_fetching_models = Falseifself.root.winfo_exists():self.root.after(0lambdaself.btn_refresh_models.config(state=tk.NORMAL, text="🔄 刷新模型"))def_update_model_list(self, models):        current_model = self.model_name.get()self.combo_model["values"] = sorted(models)if current_model in models:self.combo_model.set(current_model)elif models:self.combo_model.current(0)self._set_status(f"✅ 成功获取 {len(models)} 个模型(可在下拉列表中选择或手动输入)")# ----------------------------------------------------------# 辅助# ----------------------------------------------------------def_toggle_key_visibility(self):ifself.entry_api_key["show"] == "●":self.entry_api_key.config(show="")self.btn_show_key.config(text="🔒")else:self.entry_api_key.config(show="●")self.btn_show_key.config(text="👁")def_set_status(self, text):self.status_var.set(text)self.root.update_idletasks()def_update_nav(self):        n = len(self.generated_images)if n == 0:self.lbl_nav.config(text="")self.btn_prev.config(state=tk.DISABLED)self.btn_next.config(state=tk.DISABLED)self.btn_save.config(state=tk.DISABLED)self.btn_save_all.config(state=tk.DISABLED)elif n == 1:self.lbl_nav.config(text="1 / 1")self.btn_prev.config(state=tk.DISABLED)self.btn_next.config(state=tk.DISABLED)self.btn_save.config(state=tk.NORMAL)self.btn_save_all.config(state=tk.DISABLED)else:            idx = self.current_image_index + 1self.lbl_nav.config(text=f"{idx} / {n}")self.btn_prev.config(state=tk.NORMAL ifself.current_image_index > 0else tk.DISABLED)self.btn_next.config(state=tk.NORMAL ifself.current_image_index < n - 1else tk.DISABLED)self.btn_save.config(state=tk.NORMAL)self.btn_save_all.config(state=tk.NORMAL)def_display_image(self, img: Image.Image):self.canvas.delete("all")        cw = self.canvas.winfo_width()        ch = self.canvas.winfo_height()if cw < 10or ch < 10:            cw, ch = 800400        img_w, img_h = img.size        scale = min(cw / img_w, ch / img_h, 1.0)        new_w = max(1int(img_w * scale))        new_h = max(1int(img_h * scale))        resized = img.resize((new_w, new_h), Image.LANCZOS)self._tk_photo = ImageTk.PhotoImage(resized)        x = (cw - new_w) // 2        y = (ch - new_h) // 2self.canvas.create_image(x, y, anchor=tk.NW, image=self._tk_photo)def_on_canvas_resize(self, event=None):ifself.generated_images:self._display_image(self.generated_images[self.current_image_index])# ----------------------------------------------------------# 生成逻辑# ----------------------------------------------------------def_on_generate(self):ifself.is_generating:return        api_base = self.api_base.get().strip()        api_key = self.api_key.get().strip()        model = self.model_name.get().strip()        prompt = self.txt_prompt.get("1.0", tk.END).strip()ifnot api_base:            messagebox.showwarning("提示""请填写 API 网址")returnifnot api_key:            messagebox.showwarning("提示""请填写 API Key")returnifnot model:            messagebox.showwarning("提示""请填写或选择模型名称")returnifnot prompt:            messagebox.showwarning("提示""请输入绘图描述")returnself.is_generating = Trueself.btn_generate.config(state=tk.DISABLED, text="⏳ 生成中...")self._set_status("正在生成图片,请稍候...")        preset_name = self.combo_preset.get()        preset_cfg = PRESET_MODELS.get(preset_name, {})        thread = threading.Thread(            target=self._generate_thread,            args=(api_base, api_key, model, prompt, preset_cfg, preset_name),            daemon=True,        )        thread.start()def_generate_thread(self, api_base, api_key, model, prompt, preset_cfg, preset_name):try:            images = self._call_api(api_base, api_key, model, prompt, preset_cfg, preset_name)if images:self.generated_images = imagesself.current_image_index = 0ifself.root.winfo_exists():self.root.after(0self._show_result, images)else:ifself.root.winfo_exists():self.root.after(0self._show_error, "未生成任何图片,请检查参数或 API 响应。")except Exception as e:ifself.root.winfo_exists():self.root.after(0self._show_error, str(e))finally:self.is_generating = Falseifself.root.winfo_exists():self.root.after(0self._reset_generate_button)def_call_api(self, api_base, api_key, model, prompt, preset_cfg, preset_name) -> list:        url = api_base.rstrip("/") + "/images/generations"        headers = {"Content-Type""application/json","Authorization"f"Bearer {api_key}",        }        body = {"model": model,"prompt": prompt,"n"self.n_var.get(),"size"self.image_size.get().replace("*""x"),        }# 基础参数 (DALL-E 专属)if preset_cfg.get("style_options"):            body["style"] = self.image_style.get()if preset_cfg.get("quality_options"):            body["quality"] = self.image_quality.get()        body["response_format"] = "b64_json"# 高级参数 (仅对非 DALL-E 模型生效,防止 API 报无效参数错误)        is_dalle = "DALL-E"in preset_nameifnot is_dalle:# 种子数            seed_str = self.seed_var.get().strip()if seed_str:try:                    body["seed"] = int(seed_str)except ValueError:pass# 忽略非数字输入# 推理步数            steps = self.steps_var.get()if steps > 0:                body["num_inference_steps"] = steps# 引导比例            guidance = self.guidance_var.get()if guidance > 0:                body["guidance_scale"] = guidance# 反向提示词            neg_prompt = self.negative_prompt_var.get().strip()if neg_prompt:                body["negative_prompt"] = neg_prompt# 提示词增强ifself.enhance_var.get():                body["prompt_enhancement"] = Trueelse:# DALL-E 3 近期也支持了 seed 参数,可以加上            seed_str = self.seed_var.get().strip()if seed_str:try:                    body["seed"] = int(seed_str)except ValueError:pass        resp = requests.post(url, headers=headers, json=body, timeout=120)if resp.status_code != 200:try:                err_data = resp.json()                msg = err_data.get("error", {}).get("message"str(err_data))except:                msg = resp.textraise Exception(f"API 错误 ({resp.status_code}): {msg}")        data = resp.json()        images = []for item in data.get("data", []):if"b64_json"in item:                img_data = base64.b64decode(item["b64_json"])                img = Image.open(BytesIO(img_data))# 修复 libpng icc profile 警告if"icc_profile"in img.info:del img.info["icc_profile"]                img = img.convert("RGBA")                images.append(img)elif"url"in item:                img_resp = requests.get(item["url"], timeout=60)                img_resp.raise_for_status()                img = Image.open(BytesIO(img_resp.content))# 修复 libpng icc profile 警告if"icc_profile"in img.info:del img.info["icc_profile"]                img = img.convert("RGBA")                images.append(img)return imagesdef_show_result(self, images):self._display_image(images[0])self._update_nav()self._set_status(f"✅ 成功生成 {len(images)} 张图片")def_show_error(self, msg):        messagebox.showerror("生成失败", msg)self._set_status(f"❌ 生成失败:{msg}")def_reset_generate_button(self):self.btn_generate.config(state=tk.NORMAL, text="✨ 开始生成")# ----------------------------------------------------------# 图片导航# ----------------------------------------------------------def_on_prev_image(self):ifself.current_image_index > 0:self.current_image_index -= 1self._display_image(self.generated_images[self.current_image_index])self._update_nav()def_on_next_image(self):ifself.current_image_index < len(self.generated_images) - 1:self.current_image_index += 1self._display_image(self.generated_images[self.current_image_index])self._update_nav()# ----------------------------------------------------------# 保存# ----------------------------------------------------------def_on_save(self):ifnotself.generated_images:return        img = self.generated_images[self.current_image_index]self._save_single_image(img, self.current_image_index)def_on_save_all(self):ifnotself.generated_images:return        folder = filedialog.askdirectory(title="选择保存目录", initialdir=str(self.output_dir))ifnot folder:return        saved = []for i, img inenumerate(self.generated_images):            ts = datetime.now().strftime("%Y%m%d_%H%M%S")            filename = f"ai_image_{ts}_{i + 1}.png"            filepath = Path(folder) / filename            img.save(str(filepath), "PNG")            saved.append(str(filepath))self._set_status(f"✅ 已保存 {len(saved)} 张图片到 {folder}")        messagebox.showinfo("保存成功"f"已保存 {len(saved)} 张图片到:\n{folder}")def_save_single_image(self, img, index):        filepath = filedialog.asksaveasfilename(            title="保存图片",            initialdir=str(self.output_dir),            initialfile=f"ai_image_{index + 1}.png",            defaultextension=".png",            filetypes=[                ("PNG 图片""*.png"),                ("JPEG 图片""*.jpg"),                ("WebP 图片""*.webp"),                ("所有文件""*.*"),            ],        )ifnot filepath:return        fmt = Path(filepath).suffix.lstrip(".").upper()if fmt == "JPG":            fmt = "JPEG"            img = img.convert("RGB")        img.save(filepath, fmt)self._set_status(f"✅ 已保存: {filepath}")# ----------------------------------------------------------# 关闭# ----------------------------------------------------------def_on_close(self):# 1. 保存当前正在使用的预设配置ifself._current_preset_name:self._save_current_preset_config(self._current_preset_name)# 2. 将整体配置写入文件        config_to_save = {"last_preset"self._current_preset_name,"presets_config"self.presets_config        }        save_config(config_to_save)self.root.destroy()# ============================================================# 入口# ============================================================defmain():# Windows 高分屏 DPI 适配try:from ctypes import windll        windll.shcore.SetProcessDpiAwareness(2)except Exception:try:            windll.user32.SetProcessDPIAware()except Exception:pass# 1. 先创建主窗口    root = tk.Tk()# 2. 隐藏主窗口,避免在构建控件时出现从小变大的闪烁    root.withdraw()# 3. 设置图标try:        ico_path = resource_path("snow.ico")if os.path.exists(ico_path):            root.iconbitmap(ico_path)except Exception as e:print(f"设置图标失败: {e}")# 4. 初始化应用并构建 UI    app = AIImageGenerator(root)# 5. 强制 Tkinter 计算并更新所有布局和尺寸    root.update_idletasks()# 6. 将窗口居中显示    sw = root.winfo_screenwidth()    sh = root.winfo_screenheight()    ww = root.winfo_reqwidth()    wh = root.winfo_reqheight()    x = (sw - ww) // 2    y = (sh - wh) // 2    root.geometry(f"+{x}+{y}")# 7. 一切准备就绪,显示主窗口    root.deiconify()# 8. 进入主循环    root.mainloop()if __name__ == "__main__":    main()
基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-05-09 05:04:33 HTTP/1.1 GET : https://www.yeyulingfeng.com/a/591168.html
  2. 运行时间 : 0.107115s [ 吞吐率:9.34req/s ] 内存消耗:5,110.58kb 文件加载:145
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=385ee4c63fdcd82395558f7f6ad1bf9b
  1. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/composer/autoload_static.php ( 6.05 KB )
  7. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/ralouphie/getallheaders/src/getallheaders.php ( 1.60 KB )
  10. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  11. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  12. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  13. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  14. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  15. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  16. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  17. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  18. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  19. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions_include.php ( 0.16 KB )
  21. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/guzzlehttp/guzzle/src/functions.php ( 5.54 KB )
  22. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  23. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  24. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  25. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/provider.php ( 0.19 KB )
  26. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  27. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  28. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  29. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/common.php ( 0.03 KB )
  30. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  32. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/alipay.php ( 3.59 KB )
  33. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  34. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/app.php ( 0.95 KB )
  35. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cache.php ( 0.78 KB )
  36. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/console.php ( 0.23 KB )
  37. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/cookie.php ( 0.56 KB )
  38. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/database.php ( 2.48 KB )
  39. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/filesystem.php ( 0.61 KB )
  40. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/lang.php ( 0.91 KB )
  41. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/log.php ( 1.35 KB )
  42. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/middleware.php ( 0.19 KB )
  43. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/route.php ( 1.89 KB )
  44. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/session.php ( 0.57 KB )
  45. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/trace.php ( 0.34 KB )
  46. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/config/view.php ( 0.82 KB )
  47. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/event.php ( 0.25 KB )
  48. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  49. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/service.php ( 0.13 KB )
  50. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/AppService.php ( 0.26 KB )
  51. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  52. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  53. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  54. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  55. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  56. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/services.php ( 0.14 KB )
  57. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  58. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  59. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  60. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  61. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  62. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  63. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  64. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  65. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  66. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  67. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  68. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  69. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  70. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  71. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  72. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  73. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  74. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  75. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  76. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  77. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  78. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  79. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  80. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  81. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  82. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  83. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  84. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  85. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  86. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  87. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/Request.php ( 0.09 KB )
  88. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  89. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/middleware.php ( 0.25 KB )
  90. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  91. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  92. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  93. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  94. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  95. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  96. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  97. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  98. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  99. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  100. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  101. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  102. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  103. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/route/app.php ( 3.94 KB )
  104. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  105. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  106. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Index.php ( 9.87 KB )
  108. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/BaseController.php ( 2.05 KB )
  109. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  110. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  111. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  112. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  113. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  114. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  115. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  116. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  117. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  118. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  119. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  120. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  121. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  122. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  123. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  124. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  125. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  126. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  127. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  128. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  129. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  130. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  131. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  132. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  133. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  134. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  135. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/app/controller/Es.php ( 3.30 KB )
  136. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  137. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  138. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  139. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  140. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  141. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  142. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  143. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  144. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/runtime/temp/c935550e3e8a3a4c27dd94e439343fdf.php ( 31.50 KB )
  145. /yingpanguazai/ssd/ssd1/www/wwww.yeyulingfeng.com/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000686s ] mysql:host=127.0.0.1;port=3306;dbname=wenku;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000994s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000328s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000279s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000635s ]
  6. SELECT * FROM `set` [ RunTime:0.000232s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000674s ]
  8. SELECT * FROM `article` WHERE `id` = 591168 LIMIT 1 [ RunTime:0.000761s ]
  9. UPDATE `article` SET `lasttime` = 1778274273 WHERE `id` = 591168 [ RunTime:0.012110s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 64 LIMIT 1 [ RunTime:0.002084s ]
  11. SELECT * FROM `article` WHERE `id` < 591168 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000597s ]
  12. SELECT * FROM `article` WHERE `id` > 591168 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.003959s ]
  13. SELECT * FROM `article` WHERE `id` < 591168 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001073s ]
  14. SELECT * FROM `article` WHERE `id` < 591168 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.004113s ]
  15. SELECT * FROM `article` WHERE `id` < 591168 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.003278s ]
0.108864s