fix các bug theo yêu cầu https://fptsoftware362-my.sharepoint.com/❌/g/personal/nampdt_fpt_com/IQAHBJ4A9xqDTLgvt2bhukJEAdRB5LRz2hbJpTivvIiBSYM?wdExp=TEAMS-TREATMENT&web=1&isSPOFile=1&ovuser=f01e930a-b52e-42b1-b70f-a8882b5d043b%2CAnhTNM1%40fpt.com&clickparams=eyJBcHBOYW1lIjoiVGVhbXMtRGVza3RvcCIsIkFwcFZlcnNpb24iOiI0OS8yNjA4MTMxOTMxNyIsIkhhc0ZlZGVyYXRlZFVzZXIiOmZhbHNlfQ%3D%3D --------- Co-authored-by: Duy Le Huu <duylh19@fpt.com> Reviewed-on: #10 Co-authored-by: Anh Tran Nguyen Minh <anhtnm1@fpt.com>
This commit was merged in pull request #10.
This commit is contained in:
@@ -202,6 +202,14 @@ class GraphRenderer(QWidget):
|
||||
# Mark it and scan on the next visit rather than now — see
|
||||
# auto_scan_and_fit()'s docstring for why.
|
||||
self._needs_scan = True
|
||||
# ...except when this screen is the one on show. The picker lives HERE,
|
||||
# so a user changing project is already looking at the graph: there is
|
||||
# no "next visit" to defer to, and they had to press Scan by hand.
|
||||
# Deferring still applies when the change came from the Workspace
|
||||
# screen while this one is hidden, which is what it was for.
|
||||
if self.isVisible() and self.path_edit.text().strip():
|
||||
self._needs_scan = False
|
||||
self._scan()
|
||||
|
||||
# ---- helpers ---------------------------------------------------------------- #
|
||||
def _pick(self) -> None:
|
||||
|
||||
@@ -9,9 +9,11 @@ inline in ``ui/structure_graph_view.py::StructureGraphView.__init__`` (lines
|
||||
from __future__ import annotations
|
||||
|
||||
from PySide6.QtCore import Qt, Signal
|
||||
from PySide6.QtWidgets import QSplitter, QVBoxLayout, QWidget
|
||||
from PySide6.QtWidgets import (
|
||||
QHBoxLayout, QLabel, QProgressBar, QSplitter, QVBoxLayout, QWidget,
|
||||
)
|
||||
|
||||
from cowork_local.i18n import on_language_changed
|
||||
from cowork_local.i18n import on_language_changed, tr
|
||||
from cowork_local.presentation.graph.graph_qa_widget import GraphQaWidget
|
||||
from cowork_local.presentation.graph.graph_renderer import GraphRenderer
|
||||
from cowork_local.state import AppContext
|
||||
@@ -35,6 +37,9 @@ class StructureGraphView(QWidget):
|
||||
"""
|
||||
super().__init__()
|
||||
self.ctx = ctx
|
||||
# Project ma man Workspace da ap xuong lan gan nhat. None = chua ap lan
|
||||
# nao, de lan goi dau tien khong bi bo qua ke ca khi pid la chuoi rong.
|
||||
self._workspace_project = None
|
||||
|
||||
root = QVBoxLayout(self)
|
||||
self.renderer = GraphRenderer(ctx)
|
||||
@@ -50,6 +55,7 @@ class StructureGraphView(QWidget):
|
||||
self._split.setSizes(list(_COLLAPSED_SIZES_HINT))
|
||||
root.addWidget(self._split, 1)
|
||||
|
||||
self._build_busy_panel()
|
||||
on_language_changed(self._retranslate)
|
||||
|
||||
def _retranslate(self) -> None:
|
||||
@@ -72,18 +78,143 @@ class StructureGraphView(QWidget):
|
||||
self._split.setSizes(list(_COLLAPSED_SIZES_HINT))
|
||||
|
||||
# ---- public API (app.py / ui/workspace_tab.py) --------------------------- #
|
||||
# ---- panel "đang tải" ---------------------------------------------------
|
||||
def _build_busy_panel(self) -> None:
|
||||
"""Panel phủ lên khung đồ thị trong lúc nó đang được dựng.
|
||||
|
||||
Vì sao cần: ``prewarm()`` chỉ chạy 3 giây sau khi cửa sổ hiện
|
||||
(``main_window.py``), nên người dùng bấm GraphRAG trong 3 giây đầu sẽ
|
||||
gặp ``_ensure_web()`` dựng ``QWebEngineView`` ĐỒNG BỘ trên GUI thread —
|
||||
đóng băng 1-2 giây mà trước đây không có gì báo.
|
||||
"""
|
||||
self._busy = QWidget(self)
|
||||
self._busy.setObjectName("graphBusy")
|
||||
lay = QHBoxLayout(self._busy)
|
||||
lay.setContentsMargins(18, 14, 18, 14)
|
||||
lay.setSpacing(12)
|
||||
self._busy_label = QLabel()
|
||||
bar = QProgressBar()
|
||||
bar.setRange(0, 0) # chế độ vô định
|
||||
bar.setTextVisible(False)
|
||||
bar.setFixedWidth(120)
|
||||
lay.addWidget(self._busy_label)
|
||||
lay.addWidget(bar)
|
||||
self._busy.hide()
|
||||
self.renderer.graph_rendered.connect(self._hide_busy)
|
||||
# Đổi project ngay trên màn này thì renderer quét luôn, không chờ lần ghé
|
||||
# sau — nên panel phải bật theo, không thì lượt quét đó lại im lặng.
|
||||
self.renderer.project_changed.connect(self._on_project_scan_started)
|
||||
|
||||
def _ensure_scan_path(self) -> None:
|
||||
"""Điền ô đường dẫn từ project đang hoạt động nếu nó đang rỗng.
|
||||
|
||||
``GraphRenderer.auto_scan_and_fit`` thoát sớm khi ô đường dẫn rỗng,
|
||||
trong khi chính nút Scan lại có đường lùi (``path_edit.text() or
|
||||
Path.cwd()``). Bất đối xứng đó nghĩa là: vào màn thì không làm gì, bấm
|
||||
Scan thì chạy — đúng thứ người dùng phàn nàn.
|
||||
|
||||
Điền từ project đang chọn chứ KHÔNG lấy ``cwd()`` làm đường lùi như nút
|
||||
Scan: quét thư mục làm việc của tiến trình là quét một cây không liên
|
||||
quan gì tới project, và nó có thể rất lớn.
|
||||
"""
|
||||
if self.renderer.path_edit.text().strip():
|
||||
return
|
||||
ctx = getattr(self, "ctx", None)
|
||||
pid = (getattr(ctx, "active_project_id", "") or "").strip()
|
||||
if not pid or pid == "default":
|
||||
return
|
||||
try:
|
||||
from ...core.projects import load_project
|
||||
project = load_project(pid)
|
||||
except Exception: # noqa: BLE001
|
||||
return
|
||||
if project is not None:
|
||||
self.renderer.path_edit.setText(str(project.workspace_dir()))
|
||||
|
||||
def _on_project_scan_started(self) -> None:
|
||||
"""Renderer vừa đổi project. Nó chỉ quét ngay khi màn này đang mở."""
|
||||
if self.renderer.isVisible():
|
||||
self._show_busy("structure.scanning")
|
||||
|
||||
def _show_busy(self, key: str) -> None:
|
||||
"""Hiện panel và ÉP VẼ NGAY.
|
||||
|
||||
``repaint()`` chứ không ``update()``: ``update()`` chỉ xếp hàng một lượt
|
||||
vẽ cho vòng lặp sự kiện, mà vòng lặp đó sắp bị chặn — panel sẽ chỉ hiện
|
||||
ra SAU khi hết đóng băng, đúng lúc không còn cần tới nó nữa.
|
||||
"""
|
||||
self._busy_label.setText(tr(key))
|
||||
self._busy.adjustSize()
|
||||
self._center_busy()
|
||||
self._busy.show()
|
||||
self._busy.raise_()
|
||||
self._busy.repaint()
|
||||
|
||||
def _hide_busy(self) -> None:
|
||||
"""Ẩn panel khi đồ thị đã vẽ xong."""
|
||||
if getattr(self, "_busy", None) is not None:
|
||||
self._busy.hide()
|
||||
|
||||
def _center_busy(self) -> None:
|
||||
"""Giữ panel ở giữa khung, kể cả khi cửa sổ đổi kích thước."""
|
||||
busy = getattr(self, "_busy", None)
|
||||
if busy is None:
|
||||
return
|
||||
size = busy.sizeHint()
|
||||
busy.setGeometry((self.width() - size.width()) // 2,
|
||||
(self.height() - size.height()) // 2,
|
||||
size.width(), size.height())
|
||||
|
||||
def resizeEvent(self, e): # noqa: N802
|
||||
"""Đổi kích thước cửa sổ thì panel phải theo."""
|
||||
super().resizeEvent(e)
|
||||
self._center_busy()
|
||||
|
||||
def schedule_rescan(self, path: str = "") -> None:
|
||||
"""Hẹn quét lại đồ thị sau khi thư mục có thay đổi."""
|
||||
self.renderer.schedule_rescan(path)
|
||||
|
||||
def auto_scan_and_fit(self) -> None:
|
||||
"""Vào màn GraphRAG: hiện đồ thị, chỉ quét lại khi thật sự cần."""
|
||||
self.renderer.auto_scan_and_fit()
|
||||
"""Vào màn GraphRAG: hiện đồ thị, chỉ quét lại khi thật sự cần.
|
||||
|
||||
Panel "đang tải" bật TRƯỚC khi gọi xuống renderer: phần dựng khung xem
|
||||
chặn GUI thread, nên phải vẽ panel ra trước lúc đó. Nó tự ẩn khi
|
||||
``graph_rendered`` phát — bao trọn cả lượt quét chạy ở luồng nền phía
|
||||
sau, chứ không tắt ngay khi hàm này trả về.
|
||||
"""
|
||||
self._ensure_scan_path()
|
||||
da_dung_khung = self.renderer.web is not None
|
||||
self._show_busy("structure.scanning" if da_dung_khung else "structure.loading_view")
|
||||
try:
|
||||
self.renderer.auto_scan_and_fit()
|
||||
finally:
|
||||
# Khong co gi de ve (chua chon thu muc) thi graph_rendered khong phat
|
||||
# — panel se treo lai mai mai neu khong tu don o day.
|
||||
if not self.renderer.path_edit.text().strip():
|
||||
self._hide_busy()
|
||||
|
||||
def set_project(self, project_id: str) -> None:
|
||||
"""Khoá phạm vi quét vào một project (chuỗi rỗng là bỏ khoá)."""
|
||||
self.renderer.set_project(project_id)
|
||||
|
||||
def set_workspace_project(self, project_id: str) -> None:
|
||||
"""Áp project theo màn Workspace — bỏ qua nếu project KHÔNG đổi.
|
||||
|
||||
``WorkspaceTab._bind_project`` gọi xuống đây, và nó chạy lại mỗi lần
|
||||
người dùng vào lại màn Workspace (``_goto`` -> ``refresh`` ->
|
||||
``_load_current`` -> ``_bind_project``). Áp vô điều kiện thì bộ chọn
|
||||
project của chính màn GraphRAG bị kéo về giá trị của Workspace: chọn một
|
||||
project ở đây, sang tab khác rồi quay lại là mất.
|
||||
|
||||
Đổi sang project khác ở màn Workspace thì vẫn áp — cùng luật với tab Thư
|
||||
mục (``FolderTab.set_project_root``). Chỉ lần refresh trong CÙNG một
|
||||
project là không được đụng.
|
||||
"""
|
||||
if project_id == self._workspace_project:
|
||||
return
|
||||
self._workspace_project = project_id
|
||||
self.set_project(project_id)
|
||||
|
||||
def prewarm(self) -> None:
|
||||
"""Dựng sẵn khung đồ thị trước khi người dùng bấm vào, để lần mở đầu không giật."""
|
||||
self.renderer.prewarm()
|
||||
|
||||
Reference in New Issue
Block a user