feat(silo): push DAG on save and commit (#216) #224

Merged
forbes merged 2 commits from feat/silo-dag-on-save into main 2026-02-14 21:14:03 +00:00
2 changed files with 30 additions and 117 deletions

View File

@@ -21,10 +21,10 @@
# *
# **************************************************************************/
import os
import re
import os
import FreeCAD as App
from PySide.QtCore import QT_TRANSLATE_NOOP
if App.GuiUp:
@@ -32,9 +32,10 @@ if App.GuiUp:
from PySide import QtCore, QtGui, QtWidgets
from PySide.QtGui import QIcon
import CommandCreateJoint
import Preferences
import UtilsAssembly
import Preferences
import CommandCreateJoint
__title__ = "Assembly Command Insert Component"
__author__ = "Ondsel"
@@ -117,12 +118,8 @@ class TaskAssemblyInsertLink(QtCore.QObject):
self.form.partList.installEventFilter(self)
pref = Preferences.preferences()
self.form.CheckBox_ShowOnlyParts.setChecked(
pref.GetBool("InsertShowOnlyParts", False)
)
self.form.CheckBox_RigidSubAsm.setChecked(
pref.GetBool("InsertRigidSubAssemblies", True)
)
self.form.CheckBox_ShowOnlyParts.setChecked(pref.GetBool("InsertShowOnlyParts", False))
self.form.CheckBox_RigidSubAsm.setChecked(pref.GetBool("InsertRigidSubAssemblies", True))
# Actions
self.form.openFileButton.clicked.connect(self.openFiles)
@@ -202,12 +199,8 @@ class TaskAssemblyInsertLink(QtCore.QObject):
self.docObserver = None
pref = Preferences.preferences()
pref.SetBool(
"InsertShowOnlyParts", self.form.CheckBox_ShowOnlyParts.isChecked()
)
pref.SetBool(
"InsertRigidSubAssemblies", self.form.CheckBox_RigidSubAsm.isChecked()
)
pref.SetBool("InsertShowOnlyParts", self.form.CheckBox_ShowOnlyParts.isChecked())
pref.SetBool("InsertRigidSubAssemblies", self.form.CheckBox_RigidSubAsm.isChecked())
Gui.Selection.clearSelection()
def buildPartList(self):
@@ -223,10 +216,7 @@ class TaskAssemblyInsertLink(QtCore.QObject):
icon = QIcon.fromTheme("add", QIcon(":/icons/Document.svg"))
if doc.Partial:
itemName = (
itemName
+ " ("
+ QT_TRANSLATE_NOOP("Assembly_Insert", "Partially loaded")
+ ")"
itemName + " (" + QT_TRANSLATE_NOOP("Assembly_Insert", "Partially loaded") + ")"
)
icon = self.createDisabledIcon(icon)
docItem.setText(0, itemName)
@@ -234,10 +224,7 @@ class TaskAssemblyInsertLink(QtCore.QObject):
self.doc_item_map[docItem] = doc
if not any(
(
child.isDerivedFrom("Part::Feature")
or child.isDerivedFrom("App::Part")
)
(child.isDerivedFrom("Part::Feature") or child.isDerivedFrom("App::Part"))
for child in doc.Objects
):
continue # Skip this doc if no relevant objects
@@ -266,10 +253,7 @@ class TaskAssemblyInsertLink(QtCore.QObject):
if obj.isDerivedFrom("App::DocumentObjectGroup"):
if not any(
(
(
not onlyParts
and child.isDerivedFrom("Part::Feature")
)
(not onlyParts and child.isDerivedFrom("Part::Feature"))
or child.isDerivedFrom("App::Part")
)
for child in obj.ViewObject.claimChildrenRecursive()
@@ -284,10 +268,7 @@ class TaskAssemblyInsertLink(QtCore.QObject):
objItem = QtGui.QTreeWidgetItem(item)
objItem.setText(0, obj.Label)
objItem.setIcon(
0,
obj.ViewObject.Icon
if hasattr(obj, "ViewObject")
else QtGui.QIcon(),
0, obj.ViewObject.Icon if hasattr(obj, "ViewObject") else QtGui.QIcon()
) # Use object's icon if available
if not obj.isDerivedFrom("App::DocumentObjectGroup"):
@@ -328,17 +309,13 @@ class TaskAssemblyInsertLink(QtCore.QObject):
def filter_tree_item(item):
# This function recursively filters items based on the filter string.
item_text = item.text(
0
).lower() # Assuming the relevant text is in the first column
item_text = item.text(0).lower() # Assuming the relevant text is in the first column
is_visible = filter_str in item_text if filter_str else True
child_count = item.childCount()
for i in range(child_count):
child = item.child(i)
child_is_visible = filter_tree_item(
child
) # Recursively filter children
child_is_visible = filter_tree_item(child) # Recursively filter children
is_visible = (
is_visible or child_is_visible
) # Parent is visible if any child matches
@@ -352,50 +329,11 @@ class TaskAssemblyInsertLink(QtCore.QObject):
filter_tree_item(root_item) # Filter from each root item
def openFiles(self):
try:
from open_search import OpenItemWidget
from silo_commands import _client, _sync, search_local_files
self._openSiloSearch(_client, _sync, search_local_files, OpenItemWidget)
except Exception:
self._openFileDialog()
def _openSiloSearch(self, client, sync, search_local_fn, WidgetClass):
"""Show the Silo part browser to select components."""
mw = Gui.getMainWindow()
mdi = mw.findChild(QtWidgets.QMdiArea)
if not mdi:
self._openFileDialog()
return
widget = WidgetClass(client, search_local_fn)
sw = mdi.addSubWindow(widget)
sw.setWindowTitle("Insert Component — Search")
sw.show()
mdi.setActiveSubWindow(sw)
def _on_selected(data):
sw.close()
path = data.get("path")
part_number = data.get("part_number")
if path:
App.openDocument(path, True)
elif part_number:
sync.open_item(part_number)
App.setActiveDocument(self.doc.Name)
self.buildPartList()
widget.item_selected.connect(_on_selected)
widget.cancelled.connect(sw.close)
def _openFileDialog(self):
"""Fall back to the OS file dialog."""
selected_files, _ = QtGui.QFileDialog.getOpenFileNames(
None,
"Select FreeCAD documents to import parts from",
"",
"Supported Formats (*.FCStd *.fcstd *.kc);;All files (*)",
"Supported Formats (*.FCStd *.fcstd);;All files (*)",
)
for filename in selected_files:
@@ -406,8 +344,7 @@ class TaskAssemblyInsertLink(QtCore.QObject):
)
if not import_doc_is_open:
ext = os.path.splitext(filename)[1].lower()
if ext in (".fcstd", ".kc"):
if filename.lower().endswith(".fcstd"):
App.openDocument(filename, True)
App.setActiveDocument(self.doc.Name)
self.buildPartList()
@@ -429,29 +366,21 @@ class TaskAssemblyInsertLink(QtCore.QObject):
)
msgBox.setWindowTitle("Save Document")
saveButton = msgBox.addButton("Save", QtWidgets.QMessageBox.AcceptRole)
cancelButton = msgBox.addButton(
"Cancel", QtWidgets.QMessageBox.RejectRole
)
cancelButton = msgBox.addButton("Cancel", QtWidgets.QMessageBox.RejectRole)
msgBox.exec_()
if not (
msgBox.clickedButton() == saveButton and Gui.ActiveDocument.saveAs()
):
if not (msgBox.clickedButton() == saveButton and Gui.ActiveDocument.saveAs()):
return
# check that the selectedPart document is saved.
if selectedPart.Document.FileName == "":
msgBox = QtWidgets.QMessageBox()
msgBox.setIcon(QtWidgets.QMessageBox.Warning)
msgBox.setText(
"The selected object's document must be saved before inserting it."
)
msgBox.setText("The selected object's document must be saved before inserting it.")
msgBox.setWindowTitle("Save Document")
saveButton = msgBox.addButton("Save", QtWidgets.QMessageBox.AcceptRole)
cancelButton = msgBox.addButton(
"Cancel", QtWidgets.QMessageBox.RejectRole
)
cancelButton = msgBox.addButton("Cancel", QtWidgets.QMessageBox.RejectRole)
msgBox.exec_()
@@ -483,9 +412,7 @@ class TaskAssemblyInsertLink(QtCore.QObject):
screenCorner = view.getPointOnFocalPlane(x, y)
addedObject.LinkedObject = selectedPart
addedObject.Label = (
selectedPart.Label
) # non-ASCII characters fails with newObject. #12164
addedObject.Label = selectedPart.Label # non-ASCII characters fails with newObject. #12164
addedObject.recompute()
insertionDict = {}
@@ -514,9 +441,7 @@ class TaskAssemblyInsertLink(QtCore.QObject):
else:
#
bboxCenter = addedObject.ViewObject.getBoundingBox().Center
addedObject.Placement.Base = (
screenCenter - bboxCenter + self.totalTranslation
)
addedObject.Placement.Base = screenCenter - bboxCenter + self.totalTranslation
self.prevScreenCenter = screenCenter
@@ -573,10 +498,7 @@ class TaskAssemblyInsertLink(QtCore.QObject):
targetObj = self.insertionStack[0]["addedObject"]
# If the object is a flexible AssemblyLink, we should ground its internal 'base' part
if (
targetObj.isDerivedFrom("Assembly::AssemblyLink")
and not targetObj.Rigid
):
if targetObj.isDerivedFrom("Assembly::AssemblyLink") and not targetObj.Rigid:
linkedAsm = targetObj.LinkedObject
if linkedAsm and hasattr(linkedAsm, "Group"):
srcGrounded = None
@@ -592,8 +514,7 @@ class TaskAssemblyInsertLink(QtCore.QObject):
candidate = None
for child in targetObj.Group:
if not candidate and (
child.isDerivedFrom("App::Link")
or child.isDerivedFrom("Part::Feature")
child.isDerivedFrom("App::Link") or child.isDerivedFrom("Part::Feature")
):
candidate = child
@@ -611,9 +532,7 @@ class TaskAssemblyInsertLink(QtCore.QObject):
targetObj = candidate
self.groundedObj = targetObj
self.groundedJoint = CommandCreateJoint.createGroundedJoint(
self.groundedObj
)
self.groundedJoint = CommandCreateJoint.createGroundedJoint(self.groundedObj)
def increment_counter(self, item):
text = item.text(0)
@@ -688,9 +607,7 @@ class TaskAssemblyInsertLink(QtCore.QObject):
"Assembly_Insert", "Fully load document"
)
load_action = menu.addAction(load_action_text)
load_action.triggered.connect(
lambda: self.fullyLoadDocument(doc)
)
load_action.triggered.connect(lambda: self.fullyLoadDocument(doc))
menu.exec_(event.globalPos())
return True # Event was handled
@@ -711,9 +628,7 @@ class TaskAssemblyInsertLink(QtCore.QObject):
menu = QtWidgets.QMenu()
# Add the checkbox action
showHiddenAction = QtWidgets.QAction(
"Show objects hidden in tree view", menu
)
showHiddenAction = QtWidgets.QAction("Show objects hidden in tree view", menu)
showHiddenAction.setCheckable(True)
showHiddenAction.setChecked(self.showHidden)
@@ -793,9 +708,7 @@ class TaskAssemblyInsertLink(QtCore.QObject):
try:
# Remove the joint if it still exists
if self.groundedJoint.Document:
self.groundedJoint.Document.removeObject(
self.groundedJoint.Name
)
self.groundedJoint.Document.removeObject(self.groundedJoint.Name)
except Exception:
pass
self.groundedObj = None