All checks were successful
Build and Test / build (pull_request) Successful in 42m37s
Register .kc as a recognized file type alongside .FCStd: C++ changes: - Document.cpp: checkFileName() accepts .kc extension - Gui/Document.cpp: Save As and Save Copy dialogs include *.kc - CommandDoc.cpp: Merge document dialog includes *.kc - DlgProjectUtility.cpp: Project utility dialog includes *.kc Python changes: - FreeCADInit.py: register *.kc in import type system - kc_format.py: DocumentObserver that preserves silo/ ZIP entries across saves (caches before save, re-injects after save) - InitGui.py: register kc_format observer on startup Silo integration: - get_cad_file_path() generates .kc paths for new files - find_file_by_part_number() finds both .kc and .FCStd, preferring .kc - search_local_files() lists both .kc and .FCStd files The .kc format is a superset of .FCStd with a silo/ directory containing Kindred platform metadata. See docs/KC_SPECIFICATION.md.
183 lines
5.9 KiB
Python
183 lines
5.9 KiB
Python
# Kindred Create - Core Module
|
|
# GUI initialization - loads ztools and Silo workbenches
|
|
|
|
import os
|
|
import sys
|
|
|
|
import FreeCAD
|
|
import FreeCADGui
|
|
|
|
|
|
def setup_kindred_workbenches():
|
|
"""Load Kindred Create addon workbenches."""
|
|
home = FreeCAD.getHomePath()
|
|
mods_dir = os.path.join(home, "mods")
|
|
|
|
addons = [
|
|
("ztools", "ztools/ztools"),
|
|
("silo", "silo/freecad"),
|
|
]
|
|
|
|
for name, subpath in addons:
|
|
addon_path = os.path.join(mods_dir, subpath)
|
|
if os.path.isdir(addon_path):
|
|
# Ensure path is in sys.path
|
|
if addon_path not in sys.path:
|
|
sys.path.insert(0, addon_path)
|
|
|
|
# Execute InitGui.py if it exists
|
|
init_gui_file = os.path.join(addon_path, "InitGui.py")
|
|
if os.path.isfile(init_gui_file):
|
|
try:
|
|
with open(init_gui_file) as f:
|
|
exec_globals = globals().copy()
|
|
exec_globals["__file__"] = init_gui_file
|
|
exec_globals["__name__"] = name
|
|
exec(
|
|
compile(f.read(), init_gui_file, "exec"),
|
|
exec_globals,
|
|
)
|
|
FreeCAD.Console.PrintLog(f"Create: Loaded {name} workbench\n")
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintWarning(
|
|
f"Create: Failed to load {name} GUI: {e}\n"
|
|
)
|
|
|
|
|
|
setup_kindred_workbenches()
|
|
FreeCAD.Console.PrintLog("Create GUI module initialized\n")
|
|
|
|
|
|
def _register_kc_format():
|
|
"""Register .kc file format round-trip preservation."""
|
|
try:
|
|
import kc_format
|
|
|
|
kc_format.register()
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintLog(f"Create: kc_format registration skipped: {e}\n")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Silo integration enhancements
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
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"Create: Silo first-start check skipped: {e}\n")
|
|
|
|
|
|
def _register_silo_origin():
|
|
"""Register Silo as a file origin so the origin selector can offer it."""
|
|
try:
|
|
import silo_commands # noqa: F401 - registers Silo commands
|
|
import silo_origin
|
|
|
|
silo_origin.register_silo_origin()
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintLog(f"Create: Silo origin registration skipped: {e}\n")
|
|
|
|
|
|
def _setup_silo_auth_panel():
|
|
"""Dock the Silo authentication panel in the right-hand side panel."""
|
|
try:
|
|
from PySide import QtCore, QtWidgets
|
|
|
|
mw = FreeCADGui.getMainWindow()
|
|
if mw is None:
|
|
return
|
|
|
|
# Don't create duplicate panels
|
|
if mw.findChild(QtWidgets.QDockWidget, "SiloDatabaseAuth"):
|
|
return
|
|
|
|
import silo_commands
|
|
|
|
auth = silo_commands.SiloAuthDockWidget()
|
|
|
|
panel = QtWidgets.QDockWidget("Database Auth", mw)
|
|
panel.setObjectName("SiloDatabaseAuth")
|
|
panel.setWidget(auth.widget)
|
|
# Keep the auth object alive so its QTimer isn't destroyed while running
|
|
panel._auth = auth
|
|
mw.addDockWidget(QtCore.Qt.RightDockWidgetArea, panel)
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintLog(f"Create: Silo auth panel skipped: {e}\n")
|
|
|
|
|
|
def _setup_silo_activity_panel():
|
|
"""Show a dock widget with recent Silo database activity."""
|
|
try:
|
|
from PySide import QtCore, QtWidgets
|
|
|
|
mw = FreeCADGui.getMainWindow()
|
|
if mw is None:
|
|
return
|
|
|
|
# Don't create duplicate panels
|
|
if mw.findChild(QtWidgets.QDockWidget, "SiloDatabaseActivity"):
|
|
return
|
|
|
|
panel = QtWidgets.QDockWidget("Database Activity", mw)
|
|
panel.setObjectName("SiloDatabaseActivity")
|
|
|
|
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)")
|
|
|
|
panel.setWidget(widget)
|
|
mw.addDockWidget(QtCore.Qt.RightDockWidgetArea, panel)
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintLog(f"Create: Silo activity panel skipped: {e}\n")
|
|
|
|
|
|
def _check_for_updates():
|
|
"""Check for application updates in the background."""
|
|
try:
|
|
from update_checker import _run_update_check
|
|
|
|
_run_update_check()
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintLog(f"Create: Update check skipped: {e}\n")
|
|
|
|
|
|
# Defer enhancements until the GUI event loop is running
|
|
try:
|
|
from PySide.QtCore import QTimer
|
|
|
|
QTimer.singleShot(500, _register_kc_format)
|
|
QTimer.singleShot(1500, _register_silo_origin)
|
|
QTimer.singleShot(2000, _setup_silo_auth_panel)
|
|
QTimer.singleShot(3000, _check_silo_first_start)
|
|
QTimer.singleShot(4000, _setup_silo_activity_panel)
|
|
QTimer.singleShot(10000, _check_for_updates)
|
|
except Exception:
|
|
pass
|