Feature/fsg gamma team ui fix #3
@@ -416,6 +416,21 @@ class MainWindow(QMainWindow):
|
|||||||
self._update_dock_guard()
|
self._update_dock_guard()
|
||||||
self.help_agent.reposition()
|
self.help_agent.reposition()
|
||||||
self.help_agent.raise_()
|
self.help_agent.raise_()
|
||||||
|
# Build GraphRAG's browser view and first graph once the window is up
|
||||||
|
# and idle, so clicking GraphRAG does not sit on an empty view while
|
||||||
|
# both happen. 3s is after the first paint and any startup refresh.
|
||||||
|
if not getattr(self, "_graph_prewarmed", False):
|
||||||
|
self._graph_prewarmed = True
|
||||||
|
QTimer.singleShot(3000, self._prewarm_graph)
|
||||||
|
|
||||||
|
def _prewarm_graph(self) -> None:
|
||||||
|
view = getattr(self, "structure", None)
|
||||||
|
if view is None or not hasattr(view, "prewarm"):
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
view.prewarm()
|
||||||
|
except Exception: # noqa: BLE001 — a warm-up must never break the app
|
||||||
|
pass
|
||||||
|
|
||||||
# ---- i18n ----------------------------------------------------------
|
# ---- i18n ----------------------------------------------------------
|
||||||
def _retranslate(self) -> None:
|
def _retranslate(self) -> None:
|
||||||
|
|||||||
@@ -50,24 +50,39 @@ def main() -> int:
|
|||||||
win.show()
|
win.show()
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
st, w = win.structure, win.workspace
|
st, w = win.structure, win.workspace
|
||||||
fails = []
|
fails: list[str] = []
|
||||||
|
|
||||||
# first visit builds the view and scans once
|
# startup itself must not build any of it
|
||||||
win._goto(win._ROW_WORKSPACE, w._graphrag_tab_idx)
|
if st.web is not None or st._graph is not None:
|
||||||
|
fails.append("dung san do thi ngay luc khoi dong — startup phai nhe")
|
||||||
|
|
||||||
|
# the idle warm-up builds the browser view and the first graph off the
|
||||||
|
# click path (MainWindow fires this on a 3s timer; call it directly here)
|
||||||
|
win._prewarm_graph()
|
||||||
deadline = time.monotonic() + 30
|
deadline = time.monotonic() + 30
|
||||||
while st._graph is None and time.monotonic() < deadline:
|
while st._graph is None and time.monotonic() < deadline:
|
||||||
app.processEvents()
|
app.processEvents()
|
||||||
time.sleep(0.02)
|
time.sleep(0.02)
|
||||||
if st._graph is None:
|
if st._graph is None:
|
||||||
print("FAIL lan dau khong dung duoc do thi")
|
print("FAIL lam nong khong dung duoc do thi")
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
os._exit(1)
|
os._exit(1)
|
||||||
|
print(f"sau khi lam nong: web={'co' if st.web else 'chua'} do thi={'co' if st._graph else 'chua'}")
|
||||||
|
|
||||||
calls = {"scan": 0, "html": 0}
|
calls = {"scan": 0, "html": 0}
|
||||||
real_scan, real_html = st._scan, st._render_d3
|
real_scan, real_html = st._scan, st._render_d3
|
||||||
st._scan = lambda: (calls.__setitem__("scan", calls["scan"] + 1), real_scan())[1]
|
st._scan = lambda: (calls.__setitem__("scan", calls["scan"] + 1), real_scan())[1]
|
||||||
st._render_d3 = lambda: (calls.__setitem__("html", calls["html"] + 1), real_html())[1]
|
st._render_d3 = lambda: (calls.__setitem__("html", calls["html"] + 1), real_html())[1]
|
||||||
|
|
||||||
|
# ...so the click itself does nothing but show it — this is the flash
|
||||||
|
t0 = time.monotonic()
|
||||||
|
win._goto(win._ROW_WORKSPACE, w._graphrag_tab_idx)
|
||||||
|
app.processEvents()
|
||||||
|
print(f"bam vao GraphRAG: {(time.monotonic() - t0) * 1000:.0f}ms "
|
||||||
|
f"_scan={calls['scan']} setHtml={calls['html']}")
|
||||||
|
if calls["scan"] or calls["html"]:
|
||||||
|
fails.append("bam vao GraphRAG van phai quet/nap lai trang — con chop trang")
|
||||||
|
|
||||||
# re-entering an unchanged graph must not rebuild anything
|
# re-entering an unchanged graph must not rebuild anything
|
||||||
for _ in range(3):
|
for _ in range(3):
|
||||||
win._goto(win._ROW_WORKSPACE, w._project_tab_idx)
|
win._goto(win._ROW_WORKSPACE, w._project_tab_idx)
|
||||||
|
|||||||
@@ -495,10 +495,31 @@ class StructureGraphView(QWidget):
|
|||||||
f'<pre style="white-space:pre-wrap; font-family:Consolas,monospace; '
|
f'<pre style="white-space:pre-wrap; font-family:Consolas,monospace; '
|
||||||
f'font-size:12px;">{html.escape(text)}</pre>')
|
f'font-size:12px;">{html.escape(text)}</pre>')
|
||||||
|
|
||||||
|
def prewarm(self) -> None:
|
||||||
|
"""Pay for the graph view before it is clicked on, not during.
|
||||||
|
|
||||||
|
Opening GraphRAG built a QWebEngineView (~140ms) and scanned the project
|
||||||
|
(~485ms) while an empty browser sat on screen — long enough, and white
|
||||||
|
enough, to read as the app restarting itself. Called from an idle timer
|
||||||
|
after the window is up, so startup itself is unaffected; the memory the
|
||||||
|
lazy construction was saving is spent a few seconds later instead.
|
||||||
|
"""
|
||||||
|
if not _HAS_WEB or self.web is not None:
|
||||||
|
return
|
||||||
|
self._ensure_web()
|
||||||
|
if self._graph is None and self.path_edit.text().strip():
|
||||||
|
self._needs_scan = False
|
||||||
|
self._scan() # runs on a worker thread
|
||||||
|
|
||||||
def _ensure_web(self) -> None:
|
def _ensure_web(self) -> None:
|
||||||
if self.web is not None or not _HAS_WEB:
|
if self.web is not None or not _HAS_WEB:
|
||||||
return
|
return
|
||||||
self.web = QWebEngineView()
|
self.web = QWebEngineView()
|
||||||
|
# Blank the page in the app's own background first. A fresh
|
||||||
|
# QWebEngineView paints white, and on a dark theme that white rectangle
|
||||||
|
# WAS the flash — it showed for as long as the first scan took.
|
||||||
|
self.web.setHtml(
|
||||||
|
f"<body style='margin:0;background:{current_palette().bg}'></body>")
|
||||||
self._bridge = _Bridge()
|
self._bridge = _Bridge()
|
||||||
self._channel = QWebChannel()
|
self._channel = QWebChannel()
|
||||||
self._channel.registerObject("py", self._bridge)
|
self._channel.registerObject("py", self._bridge)
|
||||||
|
|||||||
Reference in New Issue
Block a user