Compare commits
2 Commits
63a6da8b6c
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6222a5181 | ||
|
|
9187622239 |
7
.mailmap
Normal file
7
.mailmap
Normal file
@@ -0,0 +1,7 @@
|
||||
forbes <contact@kindred-systems.com> forbes <joseph.forbes@kindred-systems.com>
|
||||
forbes <contact@kindred-systems.com> forbes <zoe.forbes@kindred-systems.com>
|
||||
forbes <contact@kindred-systems.com> forbes-0023 <joseph.forbes@kindred-systems.com>
|
||||
forbes <contact@kindred-systems.com> forbes-0023 <zoe.forbes@kindred-systems.com>
|
||||
forbes <contact@kindred-systems.com> josephforbes23 <joseph.forbes@kindred-systems.com>
|
||||
forbes <contact@kindred-systems.com> Zoe Forbes <forbes@copernicus-9.kindred.internal>
|
||||
forbes <contact@kindred-systems.com> admin <admin@kindred-systems.com>
|
||||
13
Init.py
Normal file
13
Init.py
Normal file
@@ -0,0 +1,13 @@
|
||||
"""Silo addon — console initialization.
|
||||
|
||||
Adds the shared silo-client package to sys.path so that
|
||||
``import silo_client`` works from silo_commands.py and other modules.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
_mod_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
_client_dir = os.path.join(_mod_dir, "silo-client")
|
||||
if os.path.isdir(_client_dir) and _client_dir not in sys.path:
|
||||
sys.path.insert(0, _client_dir)
|
||||
265
InitGui.py
Normal file
265
InitGui.py
Normal file
@@ -0,0 +1,265 @@
|
||||
"""Kindred Silo addon — GUI initialization.
|
||||
|
||||
Registers the SiloWorkbench, Silo file origin, overlay context,
|
||||
dock panels (auth + activity), document observer, and start page override.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
|
||||
FreeCAD.Console.PrintMessage("Kindred Silo InitGui.py loading...\n")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Workbench
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class SiloWorkbench(FreeCADGui.Workbench):
|
||||
"""Kindred Silo workbench for item database integration."""
|
||||
|
||||
MenuText = "Kindred Silo"
|
||||
ToolTip = "Item database and part management for Kindred Create"
|
||||
Icon = ""
|
||||
|
||||
def __init__(self):
|
||||
icon_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"freecad",
|
||||
"resources",
|
||||
"icons",
|
||||
"silo.svg",
|
||||
)
|
||||
if os.path.exists(icon_path):
|
||||
self.__class__.Icon = icon_path
|
||||
|
||||
def Initialize(self):
|
||||
"""Called when workbench is first activated."""
|
||||
import silo_commands
|
||||
|
||||
# Register Silo as a file origin in the unified origin system
|
||||
try:
|
||||
import silo_origin
|
||||
|
||||
silo_origin.register_silo_origin()
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"Could not register Silo origin: {e}\n")
|
||||
|
||||
# Silo origin toolbar — shown as an overlay on any context when the
|
||||
# active document is Silo-tracked. Registered as Unavailable so
|
||||
# EditingContextResolver controls visibility via the overlay system.
|
||||
self.silo_toolbar_commands = [
|
||||
"Silo_Commit",
|
||||
"Silo_Pull",
|
||||
"Silo_Push",
|
||||
"Separator",
|
||||
"Silo_Info",
|
||||
"Silo_BOM",
|
||||
"Silo_Jobs",
|
||||
]
|
||||
self.appendToolbar("Silo Origin", self.silo_toolbar_commands, "Unavailable")
|
||||
|
||||
# Silo menu provides admin/management commands.
|
||||
self.menu_commands = [
|
||||
"Silo_Info",
|
||||
"Silo_BOM",
|
||||
"Silo_Jobs",
|
||||
"Silo_TagProjects",
|
||||
"Silo_SetStatus",
|
||||
"Silo_Rollback",
|
||||
"Silo_SaveAsTemplate",
|
||||
"Separator",
|
||||
"Silo_Settings",
|
||||
"Silo_Auth",
|
||||
"Silo_Runners",
|
||||
"Silo_StartPanel",
|
||||
"Silo_Diag",
|
||||
]
|
||||
|
||||
self.appendMenu("Silo", self.menu_commands)
|
||||
|
||||
def Activated(self):
|
||||
"""Called when workbench is activated."""
|
||||
FreeCAD.Console.PrintMessage("Kindred Silo workbench activated\n")
|
||||
FreeCADGui.runCommand("Silo_StartPanel", 0)
|
||||
|
||||
def Deactivated(self):
|
||||
pass
|
||||
|
||||
def GetClassName(self):
|
||||
return "Gui::PythonWorkbench"
|
||||
|
||||
|
||||
FreeCADGui.addWorkbench(SiloWorkbench())
|
||||
FreeCAD.Console.PrintMessage("Silo workbench registered\n")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Silo overlay context — adds "Silo Origin" toolbar to any active context
|
||||
# when the current document is Silo-tracked.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _register_silo_overlay():
|
||||
"""Register the Silo overlay after the Silo workbench has initialised."""
|
||||
|
||||
def _silo_overlay_match():
|
||||
"""Return True if the active document is Silo-tracked."""
|
||||
try:
|
||||
doc = FreeCAD.ActiveDocument
|
||||
if not doc:
|
||||
return False
|
||||
from silo_origin import get_silo_origin
|
||||
|
||||
origin = get_silo_origin()
|
||||
return origin.ownsDocument(doc)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
try:
|
||||
from kindred_sdk import register_overlay
|
||||
|
||||
register_overlay(
|
||||
"silo", # overlay id
|
||||
["Silo Origin"], # toolbar names to append
|
||||
_silo_overlay_match, # match function
|
||||
)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"Silo overlay registration failed: {e}\n")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Document observer — builds the Silo metadata tree when .kc files open.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _register_silo_document_observer():
|
||||
"""Register the Silo document observer for .kc tree building."""
|
||||
try:
|
||||
import silo_document
|
||||
|
||||
silo_document.register()
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintLog(f"Silo: document observer registration skipped: {e}\n")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Dock panels — auth and activity widgets via SDK
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _setup_silo_auth_panel():
|
||||
"""Dock the Silo authentication panel in the right-hand side panel."""
|
||||
try:
|
||||
from kindred_sdk import register_dock_panel
|
||||
|
||||
def _factory():
|
||||
import silo_commands
|
||||
|
||||
auth = silo_commands.SiloAuthDockWidget()
|
||||
# Prevent GC of the auth timer by stashing on the widget
|
||||
auth.widget._auth = auth
|
||||
return auth.widget
|
||||
|
||||
register_dock_panel("SiloDatabaseAuth", "Database Auth", _factory)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintLog(f"Silo: auth panel skipped: {e}\n")
|
||||
|
||||
|
||||
def _setup_silo_activity_panel():
|
||||
"""Show a dock widget with recent Silo database activity."""
|
||||
try:
|
||||
from kindred_sdk import register_dock_panel
|
||||
|
||||
def _factory():
|
||||
from PySide import QtWidgets
|
||||
|
||||
widget = QtWidgets.QWidget()
|
||||
layout = QtWidgets.QVBoxLayout(widget)
|
||||
activity_list = QtWidgets.QListWidget()
|
||||
layout.addWidget(activity_list)
|
||||
|
||||
try:
|
||||
import silo_commands
|
||||
|
||||
items = silo_commands._client.list_items()
|
||||
if isinstance(items, list):
|
||||
for item in items[:20]:
|
||||
pn = item.get("part_number", "")
|
||||
desc = item.get("description", "")
|
||||
updated = item.get("updated_at", "")
|
||||
if updated:
|
||||
updated = updated[:10]
|
||||
activity_list.addItem(f"{pn} - {desc} - {updated}")
|
||||
if activity_list.count() == 0:
|
||||
activity_list.addItem("(No items in database)")
|
||||
except Exception:
|
||||
activity_list.addItem("(Unable to connect to Silo database)")
|
||||
|
||||
return widget
|
||||
|
||||
register_dock_panel("SiloDatabaseActivity", "Database Activity", _factory)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintLog(f"Silo: activity panel skipped: {e}\n")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# First-start check
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _check_silo_first_start():
|
||||
"""Show Silo settings dialog on first startup if not yet configured."""
|
||||
try:
|
||||
param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/KindredSilo")
|
||||
if not param.GetBool("FirstStartChecked", False):
|
||||
param.SetBool("FirstStartChecked", True)
|
||||
if not param.GetString("ApiUrl", ""):
|
||||
FreeCADGui.runCommand("Silo_Settings")
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintLog(f"Silo: first-start check skipped: {e}\n")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Start page override — must happen before the C++ StartLauncher fires
|
||||
# at ~100ms after GUI init.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
try:
|
||||
import silo_start
|
||||
|
||||
silo_start.register()
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"Silo Start page override failed: {e}\n")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Handle kindred:// URLs passed as command-line arguments on cold start.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def _handle_startup_urls():
|
||||
"""Process any kindred:// URLs passed as command-line arguments."""
|
||||
import sys
|
||||
|
||||
from silo_commands import handle_kindred_url
|
||||
|
||||
for arg in sys.argv[1:]:
|
||||
if arg.startswith("kindred://"):
|
||||
handle_kindred_url(arg)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Deferred setup — staggered timers for non-blocking startup
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
from PySide import QtCore as _QtCore
|
||||
|
||||
_QtCore.QTimer.singleShot(500, _handle_startup_urls)
|
||||
_QtCore.QTimer.singleShot(600, _register_silo_document_observer)
|
||||
_QtCore.QTimer.singleShot(2000, _setup_silo_auth_panel)
|
||||
_QtCore.QTimer.singleShot(2500, _register_silo_overlay)
|
||||
_QtCore.QTimer.singleShot(3000, _check_silo_first_start)
|
||||
_QtCore.QTimer.singleShot(4000, _setup_silo_activity_panel)
|
||||
25
package.xml
Normal file
25
package.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package format="1" xmlns="https://wiki.freecad.org/Package_Metadata">
|
||||
<name>silo</name>
|
||||
<description>PLM workbench for Kindred Create</description>
|
||||
<version>0.1.0</version>
|
||||
<maintainer email="development@kindred-systems.com">Kindred Systems</maintainer>
|
||||
<license file="LICENSE">MIT</license>
|
||||
<url type="repository">https://git.kindred-systems.com/kindred/silo-mod</url>
|
||||
|
||||
<content>
|
||||
<workbench>
|
||||
<classname>SiloWorkbench</classname>
|
||||
<subdirectory>freecad</subdirectory>
|
||||
</workbench>
|
||||
</content>
|
||||
|
||||
<kindred>
|
||||
<min_create_version>0.1.0</min_create_version>
|
||||
<load_priority>60</load_priority>
|
||||
<pure_python>true</pure_python>
|
||||
<dependencies>
|
||||
<dependency>sdk</dependency>
|
||||
</dependencies>
|
||||
</kindred>
|
||||
</package>
|
||||
Reference in New Issue
Block a user