Files
create/src/Mod/Create/silo_viewproviders.py
forbes 90728414a9
All checks were successful
Build and Test / build (pull_request) Successful in 29m13s
feat(create): manifest viewer — read-only MDI widget for silo/manifest.json (#38)
Add SiloManifestViewer widget that opens in an MDI subwindow when the
user double-clicks the Manifest node in the Silo tree. Displays all
manifest.json fields in a read-only QFormLayout with copy buttons for
Part UUID and Silo Instance.

New files:
- silo_viewers.py: SiloManifestViewer widget + create_viewer_widget()
  factory with _VIEWER_REGISTRY for future viewer classes

Modified files:
- silo_viewproviders.py: doubleClicked() wired to open MDI subwindow
  with deduplication via widget objectName()
- CMakeLists.txt: add silo_viewers.py to install list

Closes #38
2026-02-18 16:48:34 -06:00

114 lines
3.3 KiB
Python

"""
silo_viewproviders.py - ViewProvider proxy for Silo tree leaf nodes.
Controls tree icon, double-click behavior, and context menu for
SiloViewerObject nodes in the document tree.
"""
import os
# Icon directory — Phase 6 will add SVGs here.
_ICON_DIR = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"resources",
"icons",
)
# Map silo/ paths to icon basenames (without .svg extension).
_SILO_PATH_ICONS = {
"silo/manifest.json": "silo-manifest",
"silo/metadata.json": "silo-metadata",
"silo/history.json": "silo-history",
"silo/approvals.json": "silo-approvals",
"silo/dependencies.json": "silo-dependencies",
}
# Prefix-based fallbacks for subdirectory entries.
_SILO_PREFIX_ICONS = {
"silo/jobs/": "silo-job",
"silo/macros/": "silo-macro",
}
def _icon_for_path(silo_path):
"""Return absolute icon path for a silo/ entry, or '' if not found."""
name = _SILO_PATH_ICONS.get(silo_path)
if name is None:
for prefix, icon_name in _SILO_PREFIX_ICONS.items():
if silo_path.startswith(prefix):
name = icon_name
break
if name is None:
return ""
path = os.path.join(_ICON_DIR, f"{name}.svg")
return path if os.path.exists(path) else ""
class SiloViewerViewProvider:
"""ViewProvider proxy for SiloViewerObject leaf nodes."""
def __init__(self, vobj):
vobj.Proxy = self
self.Object = vobj.Object
def attach(self, vobj):
"""Store back-reference; called on document restore."""
self.Object = vobj.Object
def getIcon(self):
"""Return icon path based on SiloPath; '' uses FreeCAD default."""
try:
return _icon_for_path(self.Object.SiloPath)
except Exception:
return ""
def doubleClicked(self, vobj):
"""Open a read-only MDI viewer for this silo node."""
try:
import FreeCADGui
from PySide import QtWidgets
from silo_viewers import create_viewer_widget
obj = vobj.Object
widget = create_viewer_widget(obj)
if widget is None:
return False
mw = FreeCADGui.getMainWindow()
mdi = mw.findChild(QtWidgets.QMdiArea)
if mdi is None:
return False
# Reuse existing subwindow if already open for this object
target_name = widget.objectName()
for sw in mdi.subWindowList():
if sw.widget() and sw.widget().objectName() == target_name:
widget.deleteLater()
mdi.setActiveSubWindow(sw)
sw.show()
return True
sw = mdi.addSubWindow(widget)
sw.setWindowTitle(getattr(widget, "WINDOW_TITLE", "Silo Viewer"))
sw.show()
mdi.setActiveSubWindow(sw)
return True
except Exception as exc:
import FreeCAD
FreeCAD.Console.PrintWarning(
f"silo_viewproviders: doubleClicked failed: {exc}\n"
)
return False
def setupContextMenu(self, vobj, menu):
"""Phase 1: no context menu items."""
pass
def __getstate__(self):
return None
def __setstate__(self, state):
pass