修改AI生图接口、修改设备连接UI、修改LocalDimming逻辑和UI
This commit is contained in:
@@ -184,6 +184,13 @@ def create_ai_image_panel(self: "PQAutomationApp"):
|
||||
self._ai_image_tooltip = None
|
||||
self._ai_image_tooltip_label = None
|
||||
self._ai_image_tooltip_item = ""
|
||||
# 会话级参考图 URL(图生图模式):session_id -> upload_image_url
|
||||
self._ai_image_session_refs = {}
|
||||
# 本轮发送前手动上传的参考图(覆盖会话级)
|
||||
self._ai_image_pending_ref_url = ""
|
||||
self._ai_image_pending_ref_name = ""
|
||||
self._ai_image_uploading = False
|
||||
self.ai_image_ref_var = tk.StringVar(value="未设置参考图(文生图模式)")
|
||||
|
||||
container = ttk.Frame(frame, padding=10)
|
||||
container.pack(fill=tk.BOTH, expand=True)
|
||||
@@ -328,6 +335,29 @@ def create_ai_image_panel(self: "PQAutomationApp"):
|
||||
input_frame = ttk.LabelFrame(right, text="提示输入(Ctrl+Enter 发送)", padding=6)
|
||||
input_frame.pack(fill=tk.X, pady=(4, 0))
|
||||
|
||||
# 参考图行(图生图)
|
||||
ref_row = ttk.Frame(input_frame)
|
||||
ref_row.pack(fill=tk.X, pady=(0, 4))
|
||||
ttk.Label(
|
||||
ref_row, text="参考图:",
|
||||
foreground=palette["muted"], font=("微软雅黑", 9),
|
||||
).pack(side=tk.LEFT)
|
||||
self.ai_image_ref_label = ttk.Label(
|
||||
ref_row, textvariable=self.ai_image_ref_var,
|
||||
foreground=palette["fg"], font=("微软雅黑", 9),
|
||||
)
|
||||
self.ai_image_ref_label.pack(side=tk.LEFT, padx=(4, 0), fill=tk.X, expand=True)
|
||||
self.ai_image_clear_ref_btn = ttk.Button(
|
||||
ref_row, text="清除", width=6, bootstyle="secondary-outline",
|
||||
command=lambda: _clear_reference_image(self),
|
||||
)
|
||||
self.ai_image_clear_ref_btn.pack(side=tk.RIGHT, padx=(4, 0))
|
||||
self.ai_image_upload_ref_btn = ttk.Button(
|
||||
ref_row, text="上传参考图…", width=12, bootstyle="info-outline",
|
||||
command=lambda: _upload_reference_image(self),
|
||||
)
|
||||
self.ai_image_upload_ref_btn.pack(side=tk.RIGHT)
|
||||
|
||||
self.ai_image_input = tk.Text(
|
||||
input_frame,
|
||||
height=3,
|
||||
@@ -435,6 +465,20 @@ def reload_ai_image_list(self: "PQAutomationApp", auto_select_first=True):
|
||||
|
||||
self.ai_image_records = records
|
||||
sessions = _svc.group_records_by_session(self.ai_image_records)
|
||||
# \u4f1a\u8bdd\u7ea7\u53c2\u8003\u56fe\u94fe\u8def\uff1a\u4ee5\u6bcf\u4e2a\u4f1a\u8bdd\u6700\u8fd1\u4e00\u5f20\u751f\u6210\u56fe\u7684 imageUrl \u4f5c\u4e3a\u53c2\u8003
|
||||
refs_map = getattr(self, "_ai_image_session_refs", None)
|
||||
if isinstance(refs_map, dict):
|
||||
for sess in sessions:
|
||||
sid = sess.get("session_id") or ""
|
||||
if not sid or sid in refs_map:
|
||||
continue
|
||||
for r in sess.get("records") or []:
|
||||
src = ""
|
||||
if isinstance(r.extra, dict):
|
||||
src = (r.extra.get("source_url") or "").strip()
|
||||
if src:
|
||||
refs_map[sid] = src
|
||||
break
|
||||
flat = []
|
||||
current_sid = _svc.get_session_id()
|
||||
for idx, sess in enumerate(sessions, start=1):
|
||||
@@ -493,6 +537,10 @@ def reload_ai_image_list(self: "PQAutomationApp", auto_select_first=True):
|
||||
self._ai_image_list_loaded = True
|
||||
finally:
|
||||
self._ai_image_reloading = False
|
||||
try:
|
||||
_refresh_ref_label(self)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _format_session_header(index: int, sess: dict, is_current: bool) -> str:
|
||||
@@ -655,6 +703,10 @@ def _start_new_session(self: "PQAutomationApp"):
|
||||
return
|
||||
_svc.reset_session()
|
||||
self.ai_image_status_var.set("已开启新对话")
|
||||
# 新会话清除参考图(未设置时默认为文生图模式)
|
||||
self._ai_image_pending_ref_url = ""
|
||||
self._ai_image_pending_ref_name = ""
|
||||
_refresh_ref_label(self)
|
||||
# 新对话创建后不要自动选中历史图片,否则会立即把 session 切回旧会话。
|
||||
reload_ai_image_list(self, auto_select_first=False)
|
||||
try:
|
||||
@@ -667,6 +719,103 @@ def _start_new_session(self: "PQAutomationApp"):
|
||||
self.ai_image_meta_var.set("新对话已开启,等待生成图片")
|
||||
|
||||
|
||||
def _resolve_pending_ref_url(self: "PQAutomationApp") -> str:
|
||||
"""返回本次发送应使用的 upload_image_url。
|
||||
|
||||
优先使用用户在当前会话中手动上传的参考图;其次使用上一轮 imageUrl 自动链路。
|
||||
"""
|
||||
pending = (getattr(self, "_ai_image_pending_ref_url", "") or "").strip()
|
||||
if pending:
|
||||
return pending
|
||||
sid = _svc.get_session_id()
|
||||
refs = getattr(self, "_ai_image_session_refs", None) or {}
|
||||
return (refs.get(sid) or "").strip()
|
||||
|
||||
|
||||
def _refresh_ref_label(self: "PQAutomationApp"):
|
||||
"""根据当前 pending 上传 / 会话链路状态刷新参考图提示标签。"""
|
||||
var = getattr(self, "ai_image_ref_var", None)
|
||||
if var is None:
|
||||
return
|
||||
pending = (getattr(self, "_ai_image_pending_ref_url", "") or "").strip()
|
||||
if pending:
|
||||
name = getattr(self, "_ai_image_pending_ref_name", "") or "参考图"
|
||||
var.set(f"[图生图] {name}")
|
||||
return
|
||||
sid = _svc.get_session_id()
|
||||
refs = getattr(self, "_ai_image_session_refs", None) or {}
|
||||
chained = (refs.get(sid) or "").strip()
|
||||
if chained:
|
||||
# 自动链路:显示来源是"上一轮"
|
||||
var.set("[图生图] 沿用上一轮生成图为参考")
|
||||
return
|
||||
var.set("未设置参考图(文生图模式)")
|
||||
|
||||
|
||||
def _upload_reference_image(self: "PQAutomationApp"):
|
||||
"""选择本地图片并上传到后端,成功后作为本次发送的参考图。"""
|
||||
if getattr(self, "_ai_image_requesting", False):
|
||||
messagebox.showinfo("提示", "请等待当前请求完成")
|
||||
return
|
||||
if getattr(self, "_ai_image_uploading", False):
|
||||
return
|
||||
path = filedialog.askopenfilename(
|
||||
title="选择参考图(PNG/JPG/JPEG,超过限制将自动缩放)",
|
||||
filetypes=[("图片", "*.png;*.jpg;*.jpeg"), ("所有文件", "*.*")],
|
||||
)
|
||||
if not path:
|
||||
return
|
||||
|
||||
self._ai_image_uploading = True
|
||||
try:
|
||||
self.ai_image_upload_ref_btn.configure(state=tk.DISABLED, text="上传中…")
|
||||
self.ai_image_clear_ref_btn.configure(state=tk.DISABLED)
|
||||
except Exception:
|
||||
pass
|
||||
self.ai_image_status_var.set("正在上传参考图…")
|
||||
|
||||
name = os.path.basename(path)
|
||||
|
||||
def _ok(url: str):
|
||||
self.root.after(0, lambda: _on_upload_done(self, name, url, None))
|
||||
|
||||
def _err(exc: Exception):
|
||||
self.root.after(0, lambda: _on_upload_done(self, name, "", exc))
|
||||
|
||||
_svc.upload_image_async(path, on_success=_ok, on_error=_err)
|
||||
|
||||
|
||||
def _on_upload_done(self: "PQAutomationApp", name: str, url: str, exc):
|
||||
self._ai_image_uploading = False
|
||||
try:
|
||||
self.ai_image_upload_ref_btn.configure(state=tk.NORMAL, text="上传参考图…")
|
||||
self.ai_image_clear_ref_btn.configure(state=tk.NORMAL)
|
||||
except Exception:
|
||||
pass
|
||||
if exc is not None:
|
||||
self.ai_image_status_var.set(f"上传失败: {exc}")
|
||||
messagebox.showerror("上传失败", str(exc))
|
||||
return
|
||||
self._ai_image_pending_ref_url = url
|
||||
self._ai_image_pending_ref_name = name
|
||||
_refresh_ref_label(self)
|
||||
self.ai_image_status_var.set(f"参考图已上传:{name}")
|
||||
|
||||
|
||||
def _clear_reference_image(self: "PQAutomationApp"):
|
||||
"""清除手动上传的参考图,同时清除当前会话的自动链路参考。"""
|
||||
if getattr(self, "_ai_image_requesting", False):
|
||||
return
|
||||
self._ai_image_pending_ref_url = ""
|
||||
self._ai_image_pending_ref_name = ""
|
||||
sid = _svc.get_session_id()
|
||||
refs = getattr(self, "_ai_image_session_refs", None)
|
||||
if isinstance(refs, dict):
|
||||
refs.pop(sid, None)
|
||||
_refresh_ref_label(self)
|
||||
self.ai_image_status_var.set("已清除参考图,切换为文生图模式")
|
||||
|
||||
|
||||
def _session_id_for_item(self: "PQAutomationApp", item_id: str) -> str:
|
||||
session_map = getattr(self, "_ai_image_session_node_map", None) or {}
|
||||
parent = item_id
|
||||
@@ -704,6 +853,10 @@ def _switch_to_session(
|
||||
if item_id:
|
||||
_set_tree_selection(self, item_id)
|
||||
self.ai_image_status_var.set("已切换到历史对话")
|
||||
# 切换会话后刷新参考图标签(pending 仅当前会话有效,故清除)
|
||||
self._ai_image_pending_ref_url = ""
|
||||
self._ai_image_pending_ref_name = ""
|
||||
_refresh_ref_label(self)
|
||||
if show_message:
|
||||
messagebox.showinfo("提示", "已切换到所选历史对话")
|
||||
|
||||
@@ -720,6 +873,9 @@ def _update_request_progress(self: "PQAutomationApp"):
|
||||
def _send_prompt(self: "PQAutomationApp"):
|
||||
if getattr(self, "_ai_image_requesting", False):
|
||||
return
|
||||
if getattr(self, "_ai_image_uploading", False):
|
||||
messagebox.showinfo("提示", "参考图上传中,请稍后再发送")
|
||||
return
|
||||
prompt = self.ai_image_input.get("1.0", tk.END).strip()
|
||||
if not prompt:
|
||||
messagebox.showinfo("提示", "请输入内容")
|
||||
@@ -749,12 +905,17 @@ def _send_prompt(self: "PQAutomationApp"):
|
||||
)
|
||||
return
|
||||
|
||||
ref_url = _resolve_pending_ref_url(self)
|
||||
if ref_url:
|
||||
self.ai_image_status_var.set("后端处理中(图生图)…")
|
||||
|
||||
_svc.request_image_async(
|
||||
prompt,
|
||||
on_success=_success,
|
||||
on_error=_error,
|
||||
base_dir=_get_app_base_dir(self),
|
||||
cancel_event=self._ai_image_cancel_event,
|
||||
upload_image_url=ref_url or None,
|
||||
)
|
||||
|
||||
|
||||
@@ -764,6 +925,10 @@ def _set_requesting(self: "PQAutomationApp", flag: bool):
|
||||
self.ai_image_send_btn.configure(state=tk.DISABLED if flag else tk.NORMAL)
|
||||
self.ai_image_new_session_btn.configure(state=tk.DISABLED if flag else tk.NORMAL)
|
||||
self.ai_image_stop_btn.configure(state=tk.NORMAL if flag else tk.DISABLED)
|
||||
if hasattr(self, "ai_image_upload_ref_btn"):
|
||||
self.ai_image_upload_ref_btn.configure(state=tk.DISABLED if flag else tk.NORMAL)
|
||||
if hasattr(self, "ai_image_clear_ref_btn"):
|
||||
self.ai_image_clear_ref_btn.configure(state=tk.DISABLED if flag else tk.NORMAL)
|
||||
except Exception:
|
||||
pass
|
||||
if flag:
|
||||
@@ -797,9 +962,24 @@ def _on_request_done(self: "PQAutomationApp", record, exc, req_seq):
|
||||
return
|
||||
self.ai_image_status_var.set("完成")
|
||||
self.ai_image_input.delete("1.0", tk.END)
|
||||
# 多轮对话链路:本轮返回的 imageUrl 作为下一轮的参考图
|
||||
next_ref = ""
|
||||
try:
|
||||
if record is not None and isinstance(record.extra, dict):
|
||||
next_ref = (record.extra.get("source_url") or "").strip()
|
||||
except Exception:
|
||||
next_ref = ""
|
||||
sid = (record.session_id if record is not None else "") or _svc.get_session_id()
|
||||
if next_ref and sid:
|
||||
self._ai_image_session_refs[sid] = next_ref
|
||||
# 手动上传的 pending 参考图只对本次发送生效,发完清除
|
||||
self._ai_image_pending_ref_url = ""
|
||||
self._ai_image_pending_ref_name = ""
|
||||
_refresh_ref_label(self)
|
||||
reload_ai_image_list(self)
|
||||
if record is not None:
|
||||
logger.info("[AIImagePanel] 生成完成并入列 id=%s sid=%s", record.id, (record.session_id or "")[:8])
|
||||
logger.info("[AIImagePanel] 生成完成并入列 id=%s sid=%s next_ref=%s",
|
||||
record.id, (record.session_id or "")[:8], next_ref or "-")
|
||||
if record is not None and self.ai_image_records:
|
||||
item_id = _find_tree_item_by_record_id(self, record.id)
|
||||
if item_id:
|
||||
|
||||
Reference in New Issue
Block a user