223 lines
8.3 KiB
Python
223 lines
8.3 KiB
Python
from imgui_bundle import imgui, imgui_ctx
|
||
import io
|
||
import tempfile
|
||
import time
|
||
from pathlib import Path
|
||
from panda3d.core import Filename
|
||
try:
|
||
from PIL import Image
|
||
except Exception: # pragma: no cover - pillow may be missing in minimal env
|
||
Image = None
|
||
|
||
class EditorPanelsCenterMixin:
|
||
"""Auto-split mixin from editor_panels.py."""
|
||
|
||
def _on_create_web_panel(self):
|
||
"""创建或激活 ImGui Web 面板。"""
|
||
self._ensure_web_panel_state()
|
||
self.app.set_panel_visible("web", True)
|
||
|
||
webview = getattr(self.app, "_imgui_webview", None)
|
||
if webview and getattr(webview, "_running", False):
|
||
return True
|
||
|
||
return self._start_imgui_webview(self.app.web_panel_url_input)
|
||
|
||
def _ensure_web_panel_state(self):
|
||
if not hasattr(self.app, "web_panel_url_input") or not self.app.web_panel_url_input:
|
||
self.app.web_panel_url_input = "https://www.baidu.com"
|
||
if not hasattr(self.app, "_imgui_webview"):
|
||
self.app._imgui_webview = None
|
||
if not hasattr(self.app, "_imgui_webview_tex_id"):
|
||
self.app._imgui_webview_tex_id = None
|
||
if not hasattr(self.app, "_imgui_webview_texture_path"):
|
||
self.app._imgui_webview_texture_path = None
|
||
|
||
def _start_imgui_webview(self, url):
|
||
self._ensure_web_panel_state()
|
||
self._stop_imgui_webview()
|
||
try:
|
||
target_url = (url or "").strip()
|
||
if not target_url:
|
||
target_url = "https://www.example.com"
|
||
if not target_url.startswith(("http://", "https://", "file://")):
|
||
target_url = "https://" + target_url
|
||
|
||
from core.imgui_webview import ImGuiWebView
|
||
webview = ImGuiWebView(width=1280, height=720)
|
||
webview.start(target_url)
|
||
self.app._imgui_webview = webview
|
||
return True
|
||
except Exception as e:
|
||
self.app.add_error_message(f"启动Web视图失败: {e}")
|
||
return False
|
||
|
||
def _stop_imgui_webview(self):
|
||
webview = getattr(self.app, "_imgui_webview", None)
|
||
if webview:
|
||
try:
|
||
webview.stop()
|
||
except Exception:
|
||
pass
|
||
self.app._imgui_webview = None
|
||
|
||
tex_id = getattr(self.app, "_imgui_webview_tex_id", None)
|
||
if tex_id:
|
||
try:
|
||
self.app.imgui.removeTexture(tex_id)
|
||
except Exception:
|
||
pass
|
||
self.app._imgui_webview_tex_id = None
|
||
texture_path = getattr(self.app, "_imgui_webview_texture_path", None)
|
||
if texture_path:
|
||
try:
|
||
Path(texture_path).unlink(missing_ok=True)
|
||
except Exception:
|
||
pass
|
||
self.app._imgui_webview_texture_path = None
|
||
|
||
def _navigate_web_panel(self):
|
||
self._ensure_web_panel_state()
|
||
url = (self.app.web_panel_url_input or "").strip()
|
||
if not url:
|
||
return
|
||
|
||
webview = getattr(self.app, "_imgui_webview", None)
|
||
if not webview or not getattr(webview, "_running", False):
|
||
self._start_imgui_webview(url)
|
||
return
|
||
|
||
webview.navigate(url)
|
||
|
||
def _update_web_panel_texture(self):
|
||
webview = getattr(self.app, "_imgui_webview", None)
|
||
if not webview:
|
||
return
|
||
if not webview.tex_dirty:
|
||
return
|
||
|
||
jpeg_bytes = webview.get_screenshot_bytes()
|
||
if not jpeg_bytes:
|
||
return
|
||
|
||
try:
|
||
if Image is None:
|
||
self.app.add_warning_message("缺少 Pillow,无法更新Web纹理")
|
||
return
|
||
img = Image.open(io.BytesIO(jpeg_bytes)).convert("RGBA")
|
||
# p3dimgui 纹理坐标系与网页截图存在Y轴方向差异,先在像素层修正
|
||
img = img.transpose(Image.FLIP_TOP_BOTTOM)
|
||
|
||
temp_dir = Path(tempfile.gettempdir()) / "eg_imgui_webpanel"
|
||
temp_dir.mkdir(parents=True, exist_ok=True)
|
||
temp_path = temp_dir / f"webview_{time.time_ns()}.png"
|
||
img.save(temp_path, format="PNG")
|
||
|
||
panda_path = Filename.fromOsSpecific(str(temp_path)).getFullpath()
|
||
new_tex_id = self.app.imgui.loadTexture(panda_path)
|
||
old_tex_id = getattr(self.app, "_imgui_webview_tex_id", None)
|
||
if old_tex_id:
|
||
try:
|
||
self.app.imgui.removeTexture(old_tex_id)
|
||
except Exception:
|
||
pass
|
||
|
||
old_texture_path = getattr(self.app, "_imgui_webview_texture_path", None)
|
||
if old_texture_path:
|
||
try:
|
||
Path(old_texture_path).unlink(missing_ok=True)
|
||
except Exception:
|
||
pass
|
||
|
||
self.app._imgui_webview_tex_id = new_tex_id
|
||
self.app._imgui_webview_texture_path = str(temp_path)
|
||
except Exception as e:
|
||
self.app.add_warning_message(f"Web纹理更新失败: {e}")
|
||
finally:
|
||
webview.tex_dirty = False
|
||
|
||
@staticmethod
|
||
def _clamp01(value):
|
||
return 0.0 if value < 0.0 else 1.0 if value > 1.0 else value
|
||
|
||
def _draw_web_panel(self):
|
||
"""绘制 Web 面板(ImGui + 后台浏览器截图)。"""
|
||
self._ensure_web_panel_state()
|
||
if not self.app.is_panel_visible("web"):
|
||
self._stop_imgui_webview()
|
||
return
|
||
|
||
flags = self.app.style_manager.get_window_flags("panel")
|
||
with self.app.style_manager.begin_styled_window("Web面板", self.app.showWebPanel, flags) as (_, opened):
|
||
if not opened:
|
||
self.app.set_panel_visible("web", False)
|
||
self._stop_imgui_webview()
|
||
return
|
||
|
||
self.app.set_panel_visible("web", opened)
|
||
|
||
changed, self.app.web_panel_url_input = imgui.input_text(
|
||
"URL", self.app.web_panel_url_input, 1024
|
||
)
|
||
if changed:
|
||
self.app.web_panel_url_input = self.app.web_panel_url_input.strip()
|
||
|
||
imgui.same_line()
|
||
if imgui.button("访问"):
|
||
self._navigate_web_panel()
|
||
|
||
webview = getattr(self.app, "_imgui_webview", None)
|
||
if not webview:
|
||
if not self._start_imgui_webview(self.app.web_panel_url_input):
|
||
imgui.text_colored((1.0, 0.4, 0.4, 1.0), "Web视图启动失败")
|
||
return
|
||
webview = self.app._imgui_webview
|
||
|
||
imgui.same_line()
|
||
if imgui.button("后退"):
|
||
webview.go_back()
|
||
imgui.same_line()
|
||
if imgui.button("前进"):
|
||
webview.go_forward()
|
||
imgui.same_line()
|
||
if imgui.button("刷新"):
|
||
webview.reload()
|
||
|
||
current_url = webview.current_url or self.app.web_panel_url_input
|
||
if current_url:
|
||
imgui.text_colored((0.7, 0.7, 0.7, 1.0), current_url)
|
||
if webview.error:
|
||
imgui.text_colored((1.0, 0.4, 0.4, 1.0), webview.error)
|
||
|
||
imgui.separator()
|
||
|
||
self._update_web_panel_texture()
|
||
tex_id = getattr(self.app, "_imgui_webview_tex_id", None)
|
||
available = imgui.get_content_region_avail()
|
||
display_w = max(float(available.x), 64.0)
|
||
display_h = max(float(available.y), 64.0)
|
||
|
||
if tex_id:
|
||
imgui.image(tex_id, (display_w, display_h))
|
||
|
||
if imgui.is_item_hovered():
|
||
mouse_wheel = imgui.get_io().mouse_wheel
|
||
if abs(mouse_wheel) > 1e-4:
|
||
webview.scroll(-mouse_wheel * 120.0)
|
||
|
||
if imgui.is_mouse_clicked(0):
|
||
item_min = imgui.get_item_rect_min()
|
||
item_max = imgui.get_item_rect_max()
|
||
item_w = max(float(item_max.x - item_min.x), 1.0)
|
||
item_h = max(float(item_max.y - item_min.y), 1.0)
|
||
mouse_pos = imgui.get_mouse_pos()
|
||
|
||
x_ratio = self._clamp01((float(mouse_pos.x) - float(item_min.x)) / item_w)
|
||
y_ratio = self._clamp01((float(mouse_pos.y) - float(item_min.y)) / item_h)
|
||
webview.click(x_ratio, y_ratio)
|
||
elif webview.is_loading:
|
||
imgui.text("网页加载中...")
|
||
else:
|
||
imgui.text("正在初始化Web视图...")
|
||
|