|
|
|
|
@@ -21,10 +21,10 @@
|
|
|
|
|
# *
|
|
|
|
|
# **************************************************************************/
|
|
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
import os
|
|
|
|
|
import FreeCAD as App
|
|
|
|
|
import re
|
|
|
|
|
|
|
|
|
|
import FreeCAD as App
|
|
|
|
|
from PySide.QtCore import QT_TRANSLATE_NOOP
|
|
|
|
|
|
|
|
|
|
if App.GuiUp:
|
|
|
|
|
@@ -32,10 +32,9 @@ if App.GuiUp:
|
|
|
|
|
from PySide import QtCore, QtGui, QtWidgets
|
|
|
|
|
from PySide.QtGui import QIcon
|
|
|
|
|
|
|
|
|
|
import UtilsAssembly
|
|
|
|
|
import Preferences
|
|
|
|
|
import CommandCreateJoint
|
|
|
|
|
|
|
|
|
|
import Preferences
|
|
|
|
|
import UtilsAssembly
|
|
|
|
|
|
|
|
|
|
__title__ = "Assembly Command Insert Component"
|
|
|
|
|
__author__ = "Ondsel"
|
|
|
|
|
@@ -118,8 +117,12 @@ 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)
|
|
|
|
|
@@ -199,8 +202,12 @@ 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):
|
|
|
|
|
@@ -216,7 +223,10 @@ 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)
|
|
|
|
|
@@ -224,7 +234,10 @@ 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
|
|
|
|
|
@@ -253,7 +266,10 @@ 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()
|
|
|
|
|
@@ -268,7 +284,10 @@ 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"):
|
|
|
|
|
@@ -309,13 +328,17 @@ 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
|
|
|
|
|
@@ -329,11 +352,50 @@ 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);;All files (*)",
|
|
|
|
|
"Supported Formats (*.FCStd *.fcstd *.kc);;All files (*)",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for filename in selected_files:
|
|
|
|
|
@@ -344,7 +406,8 @@ class TaskAssemblyInsertLink(QtCore.QObject):
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if not import_doc_is_open:
|
|
|
|
|
if filename.lower().endswith(".fcstd"):
|
|
|
|
|
ext = os.path.splitext(filename)[1].lower()
|
|
|
|
|
if ext in (".fcstd", ".kc"):
|
|
|
|
|
App.openDocument(filename, True)
|
|
|
|
|
App.setActiveDocument(self.doc.Name)
|
|
|
|
|
self.buildPartList()
|
|
|
|
|
@@ -366,21 +429,29 @@ 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_()
|
|
|
|
|
|
|
|
|
|
@@ -412,7 +483,9 @@ 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 = {}
|
|
|
|
|
@@ -441,7 +514,9 @@ 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
|
|
|
|
|
|
|
|
|
|
@@ -498,7 +573,10 @@ 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
|
|
|
|
|
@@ -514,7 +592,8 @@ 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
|
|
|
|
|
|
|
|
|
|
@@ -532,7 +611,9 @@ 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)
|
|
|
|
|
@@ -607,7 +688,9 @@ 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
|
|
|
|
|
|
|
|
|
|
@@ -628,7 +711,9 @@ 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)
|
|
|
|
|
|
|
|
|
|
@@ -708,7 +793,9 @@ 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
|
|
|
|
|
|