Assembly: Insert new part (#17922)

* Assembly: Joint Object : encapsulate the joint creation widget such that the task can be subclassed and ui customized by other commands.

* Assembly: Insert New Part

* Update src/Mod/Assembly/CommandInsertNewPart.py

Co-authored-by: Kacper Donat <kadet1090@gmail.com>

---------

Co-authored-by: Kacper Donat <kadet1090@gmail.com>
This commit is contained in:
PaddleStroke
2024-12-02 18:34:02 +01:00
committed by GitHub
parent 7b7db5be0e
commit f6268ffd28
6 changed files with 400 additions and 150 deletions

View File

@@ -9,6 +9,7 @@ set(Assembly_Scripts
CommandCreateAssembly.py
CommandCreateBom.py
CommandInsertLink.py
CommandInsertNewPart.py
CommandSolveAssembly.py
CommandCreateJoint.py
CommandCreateView.py

View File

@@ -43,6 +43,43 @@ __author__ = "Ondsel"
__url__ = "https://www.freecad.org"
tooltip = (
"<p>"
+ QT_TRANSLATE_NOOP(
"Assembly_InsertLink",
"Insert a component into the active assembly. This will create dynamic links to parts, bodies, primitives, and assemblies. To insert external components, make sure that the file is <b>open in the current session</b>",
)
+ "</p><p><ul><li>"
+ QT_TRANSLATE_NOOP("Assembly_InsertLink", "Insert by left clicking items in the list.")
+ "</li><li>"
+ QT_TRANSLATE_NOOP("Assembly_InsertLink", "Remove by right clicking items in the list.")
+ "</li><li>"
+ QT_TRANSLATE_NOOP(
"Assembly_InsertLink",
"Press shift to add several instances of the component while clicking on the view.",
)
+ "</li></ul></p>"
)
class CommandGroupInsert:
def GetCommands(self):
return ("Assembly_InsertLink", "Assembly_InsertNewPart")
def GetResources(self):
"""Set icon, menu and tooltip."""
return {
"Pixmap": "Assembly_InsertLink",
"MenuText": QT_TRANSLATE_NOOP("Assembly_Insert", "Insert"),
"ToolTip": tooltip,
"CmdType": "ForEdit",
}
def IsActive(self):
return UtilsAssembly.isAssemblyCommandActive()
class CommandInsertLink:
def __init__(self):
pass
@@ -52,23 +89,7 @@ class CommandInsertLink:
"Pixmap": "Assembly_InsertLink",
"MenuText": QT_TRANSLATE_NOOP("Assembly_InsertLink", "Insert Component"),
"Accel": "I",
"ToolTip": "<p>"
+ QT_TRANSLATE_NOOP(
"Assembly_InsertLink",
"Insert a component into the active assembly. This will create dynamic links to parts, bodies, primitives, and assemblies. To insert external components, make sure that the file is <b>open in the current session</b>",
)
+ "</p><p><ul><li>"
+ QT_TRANSLATE_NOOP("Assembly_InsertLink", "Insert by left clicking items in the list.")
+ "</li><li>"
+ QT_TRANSLATE_NOOP(
"Assembly_InsertLink", "Remove by right clicking items in the list."
)
+ "</li><li>"
+ QT_TRANSLATE_NOOP(
"Assembly_InsertLink",
"Press shift to add several instances of the component while clicking on the view.",
)
+ "</li></ul></p>",
"ToolTip": tooltip,
"CmdType": "ForEdit",
}
@@ -611,3 +632,4 @@ class TaskAssemblyInsertLink(QtCore.QObject):
if App.GuiUp:
Gui.addCommand("Assembly_InsertLink", CommandInsertLink())
Gui.addCommand("Assembly_Insert", CommandGroupInsert())

View File

@@ -0,0 +1,194 @@
# SPDX-License-Identifier: LGPL-2.1-or-later
# /**************************************************************************
# *
# Copyright (c) 2024 Ondsel <development@ondsel.com> *
# *
# This file is part of FreeCAD. *
# *
# FreeCAD is free software: you can redistribute it and/or modify it *
# under the terms of the GNU Lesser General Public License as *
# published by the Free Software Foundation, either version 2.1 of the *
# License, or (at your option) any later version. *
# *
# FreeCAD is distributed in the hope that it will be useful, but *
# WITHOUT ANY WARRANTY; without even the implied warranty of *
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
# Lesser General Public License for more details. *
# *
# You should have received a copy of the GNU Lesser General Public *
# License along with FreeCAD. If not, see *
# <https://www.gnu.org/licenses/>. *
# *
# **************************************************************************/
import re
import os
import FreeCAD as App
from PySide.QtCore import QT_TRANSLATE_NOOP
if App.GuiUp:
import FreeCADGui as Gui
from PySide import QtCore, QtGui, QtWidgets
from PySide.QtGui import QIcon
from PySide.QtCore import QTimer
import UtilsAssembly
import Preferences
import JointObject
translate = App.Qt.translate
__title__ = "Assembly Command Insert New Part"
__author__ = "Ondsel"
__url__ = "https://www.freecad.org"
class CommandInsertNewPart:
def __init__(self):
pass
def GetResources(self):
return {
"Pixmap": "Geofeaturegroup",
"MenuText": QT_TRANSLATE_NOOP("Assembly_InsertNewPart", "Insert a new part"),
"Accel": "P",
"ToolTip": "<p>"
+ QT_TRANSLATE_NOOP(
"Assembly_InsertNewPart",
"Insert a new part into the active assembly. The new part's origin can be positioned in the assembly.",
)
+ "</p>",
"CmdType": "ForEdit",
}
def IsActive(self):
return UtilsAssembly.isAssemblyCommandActive()
def Activated(self):
panel = TaskAssemblyNewPart()
Gui.Control.showDialog(panel)
class TaskAssemblyNewPart(JointObject.TaskAssemblyCreateJoint):
def __init__(self):
super().__init__(0, None, True)
self.assembly = UtilsAssembly.activeAssembly()
# Retrieve the existing layout of `self.form`
mainLayout = self.form.layout()
# Add a name input
nameLayout = QtWidgets.QHBoxLayout()
nameLabel = QtWidgets.QLabel(translate("Assembly", "Part name"))
self.nameEdit = QtWidgets.QLineEdit()
nameLayout.addWidget(nameLabel)
nameLayout.addWidget(self.nameEdit)
mainLayout.addLayout(nameLayout)
self.nameEdit.setText(translate("Assembly", "Part"))
# Add a checkbox
self.createInNewFileCheck = QtWidgets.QCheckBox(
translate("Assembly", "Create part in new file")
)
mainLayout.addWidget(self.createInNewFileCheck)
self.createInNewFileCheck.setChecked(
Preferences.preferences().GetBool("PartInNewFile", True)
)
# Wrap the joint creation UI in a groupbox
jointGroupBox = QtWidgets.QGroupBox(translate("Assembly", "Joint new part origin"))
jointLayout = QtWidgets.QVBoxLayout(jointGroupBox)
jointLayout.addWidget(self.jForm)
jointLayout.setContentsMargins(0, 0, 0, 0)
jointLayout.setSpacing(0)
mainLayout.addWidget(jointGroupBox)
self.link = self.assembly.newObject("App::Link", "Link")
# add the link as the first object of the joint
Gui.Selection.addSelection(
self.assembly.Document.Name, self.assembly.Name, self.link.Name + "."
)
def createPart(self):
partName = self.nameEdit.text()
newFile = self.createInNewFileCheck.isChecked()
doc = self.assembly.Document
if newFile:
doc = App.newDocument(partName)
part, body = UtilsAssembly.createPart(partName, doc)
App.setActiveDocument(self.assembly.Document.Name)
# Then we need to link the part.
if newFile:
# New file must be saved or we can't link
if not Gui.getDocument(doc).saveAs():
msgBox = QtWidgets.QMessageBox()
msgBox.setIcon(QtWidgets.QMessageBox.Warning)
msgBox.setText(
"If the new document is not saved the new part cannot be linked in the assembly."
)
msgBox.setWindowTitle(translate("Assembly", "Save Document"))
saveButton = msgBox.addButton(
translate("Assembly", "Save"), QtWidgets.QMessageBox.AcceptRole
)
cancelButton = msgBox.addButton(
translate("Assembly", "Don't link"), QtWidgets.QMessageBox.RejectRole
)
msgBox.exec_()
if not (msgBox.clickedButton() == saveButton and Gui.getDocument(doc).saveAs()):
return
self.link.LinkedObject = part
self.link.touch()
self.link.Label = part.Label
# Set the body as active in the assembly doc
self.expandLinkManually(self.link)
doc = self.assembly.Document
Gui.getDocument(doc).ActiveView.setActiveObject("pdbody", body)
doc.recompute()
def expandLinkManually(self, link):
# Shoud not be necessary
# This is a workaround of https://github.com/FreeCAD/FreeCAD/issues/17904
mw = Gui.getMainWindow()
trees = mw.findChildren(QtGui.QTreeWidget)
Gui.Selection.addSelection(link)
for tree in trees:
for item in tree.selectedItems():
tree.expandItem(item)
def accept(self):
if len(self.refs) != 2:
# if the joint is not complete we cancel the joint but not the new part!
self.joint.Document.removeObject(self.joint.Name)
else:
JointObject.solveIfAllowed(self.assembly)
self.joint.Visibility = False
cmds = UtilsAssembly.generatePropertySettings(self.joint)
Gui.doCommand(cmds)
self.createPart()
self.deactivate()
App.closeActiveTransaction()
return True
def deactivate(self):
Preferences.preferences().SetBool("PartInNewFile", self.createInNewFileCheck.isChecked())
super().deactivate()
if App.GuiUp:
Gui.addCommand("Assembly_InsertNewPart", CommandInsertNewPart())

View File

@@ -63,7 +63,7 @@ class AssemblyWorkbench(Workbench):
# load the builtin modules
from PySide import QtCore, QtGui
from PySide.QtCore import QT_TRANSLATE_NOOP
import CommandCreateAssembly, CommandInsertLink, CommandCreateJoint, CommandSolveAssembly, CommandExportASMT, CommandCreateView, CommandCreateBom
import CommandCreateAssembly, CommandInsertLink, CommandInsertNewPart, CommandCreateJoint, CommandSolveAssembly, CommandExportASMT, CommandCreateView, CommandCreateBom
import Preferences
FreeCADGui.addLanguagePath(":/translations")
@@ -76,7 +76,7 @@ class AssemblyWorkbench(Workbench):
# build commands list
cmdList = [
"Assembly_CreateAssembly",
"Assembly_InsertLink",
"Assembly_Insert",
"Assembly_SolveAssembly",
"Assembly_CreateView",
"Assembly_CreateBom",

View File

@@ -32,6 +32,7 @@ from collections.abc import Sequence
if App.GuiUp:
import FreeCADGui as Gui
from PySide import QtWidgets
__title__ = "Assembly Joint object"
__author__ = "Ondsel"
@@ -1170,6 +1171,9 @@ class MakeJointSelGate:
):
if UtilsAssembly.isLink(selected_object):
linked = selected_object.getLinkedObject()
if linked == selected_object:
# We accept empty links
return True
if not (linked.isDerivedFrom("Part::Feature") or linked.isDerivedFrom("App::Part")):
return False
@@ -1183,7 +1187,7 @@ activeTask = None
class TaskAssemblyCreateJoint(QtCore.QObject):
def __init__(self, jointTypeIndex, jointObj=None):
def __init__(self, jointTypeIndex, jointObj=None, subclass=False):
super().__init__()
global activeTask
@@ -1210,22 +1214,33 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.assembly.ViewObject.MoveOnlyPreselected = True
self.assembly.ViewObject.MoveInCommand = False
self.form = Gui.PySideUic.loadUi(":/panels/TaskAssemblyCreateJoint.ui")
# Create a top-level container widget for subclasses of TaskAssemblyCreateJoint
self.form = QtWidgets.QWidget()
# Load the joint creation UI and parent it to `self.form`
self.jForm = Gui.PySideUic.loadUi(":/panels/TaskAssemblyCreateJoint.ui", self.form)
# Create a layout for `self.form` and add `self.jForm` to it
layout = QtWidgets.QVBoxLayout(self.form)
if not subclass:
layout.setContentsMargins(0, 0, 0, 0)
layout.setSpacing(0)
layout.addWidget(self.jForm)
if self.activeType == "Part":
self.form.setWindowTitle("Match parts")
self.form.jointType.hide()
self.jForm.setWindowTitle("Match parts")
self.jForm.jointType.hide()
self.form.jointType.addItems(TranslatedJointTypes)
self.jForm.jointType.addItems(TranslatedJointTypes)
self.form.jointType.setCurrentIndex(jointTypeIndex)
self.jType = JointTypes[self.form.jointType.currentIndex()]
self.form.jointType.currentIndexChanged.connect(self.onJointTypeChanged)
self.jForm.jointType.setCurrentIndex(jointTypeIndex)
self.jType = JointTypes[self.jForm.jointType.currentIndex()]
self.jForm.jointType.currentIndexChanged.connect(self.onJointTypeChanged)
self.form.reverseRotCheckbox.setChecked(self.jType == "Gears")
self.form.reverseRotCheckbox.stateChanged.connect(self.reverseRotToggled)
self.jForm.reverseRotCheckbox.setChecked(self.jType == "Gears")
self.jForm.reverseRotCheckbox.stateChanged.connect(self.reverseRotToggled)
self.form.advancedOffsetCheckbox.stateChanged.connect(self.advancedOffsetToggled)
self.jForm.advancedOffsetCheckbox.stateChanged.connect(self.advancedOffsetToggled)
if jointObj:
Gui.Selection.clearSelection()
@@ -1240,7 +1255,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
else:
self.creating = True
self.jointName = self.form.jointType.currentText().replace(" ", "")
self.jointName = self.jForm.jointType.currentText().replace(" ", "")
if self.activeType == "Part":
App.setActiveTransaction("Transform")
else:
@@ -1254,33 +1269,33 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.adaptUi()
self.form.distanceSpinbox.valueChanged.connect(self.onDistanceChanged)
self.form.distanceSpinbox2.valueChanged.connect(self.onDistance2Changed)
self.form.offsetSpinbox.valueChanged.connect(self.onOffsetChanged)
self.form.rotationSpinbox.valueChanged.connect(self.onRotationChanged)
bind = Gui.ExpressionBinding(self.form.distanceSpinbox).bind(self.joint, "Distance")
bind = Gui.ExpressionBinding(self.form.distanceSpinbox2).bind(self.joint, "Distance2")
bind = Gui.ExpressionBinding(self.form.offsetSpinbox).bind(self.joint, "Offset2.Base.z")
bind = Gui.ExpressionBinding(self.form.rotationSpinbox).bind(
self.jForm.distanceSpinbox.valueChanged.connect(self.onDistanceChanged)
self.jForm.distanceSpinbox2.valueChanged.connect(self.onDistance2Changed)
self.jForm.offsetSpinbox.valueChanged.connect(self.onOffsetChanged)
self.jForm.rotationSpinbox.valueChanged.connect(self.onRotationChanged)
bind = Gui.ExpressionBinding(self.jForm.distanceSpinbox).bind(self.joint, "Distance")
bind = Gui.ExpressionBinding(self.jForm.distanceSpinbox2).bind(self.joint, "Distance2")
bind = Gui.ExpressionBinding(self.jForm.offsetSpinbox).bind(self.joint, "Offset2.Base.z")
bind = Gui.ExpressionBinding(self.jForm.rotationSpinbox).bind(
self.joint, "Offset2.Rotation.Yaw"
)
self.form.offset1Button.clicked.connect(self.onOffset1Clicked)
self.form.offset2Button.clicked.connect(self.onOffset2Clicked)
self.form.PushButtonReverse.clicked.connect(self.onReverseClicked)
self.jForm.offset1Button.clicked.connect(self.onOffset1Clicked)
self.jForm.offset2Button.clicked.connect(self.onOffset2Clicked)
self.jForm.PushButtonReverse.clicked.connect(self.onReverseClicked)
self.form.limitCheckbox1.stateChanged.connect(self.adaptUi)
self.form.limitCheckbox2.stateChanged.connect(self.adaptUi)
self.form.limitCheckbox3.stateChanged.connect(self.adaptUi)
self.form.limitCheckbox4.stateChanged.connect(self.adaptUi)
self.jForm.limitCheckbox1.stateChanged.connect(self.adaptUi)
self.jForm.limitCheckbox2.stateChanged.connect(self.adaptUi)
self.jForm.limitCheckbox3.stateChanged.connect(self.adaptUi)
self.jForm.limitCheckbox4.stateChanged.connect(self.adaptUi)
self.form.limitLenMinSpinbox.valueChanged.connect(self.onLimitLenMinChanged)
self.form.limitLenMaxSpinbox.valueChanged.connect(self.onLimitLenMaxChanged)
self.form.limitRotMinSpinbox.valueChanged.connect(self.onLimitRotMinChanged)
self.form.limitRotMaxSpinbox.valueChanged.connect(self.onLimitRotMaxChanged)
bind = Gui.ExpressionBinding(self.form.limitLenMinSpinbox).bind(self.joint, "LengthMin")
bind = Gui.ExpressionBinding(self.form.limitLenMaxSpinbox).bind(self.joint, "LengthMax")
bind = Gui.ExpressionBinding(self.form.limitRotMinSpinbox).bind(self.joint, "AngleMin")
bind = Gui.ExpressionBinding(self.form.limitRotMaxSpinbox).bind(self.joint, "AngleMax")
self.jForm.limitLenMinSpinbox.valueChanged.connect(self.onLimitLenMinChanged)
self.jForm.limitLenMaxSpinbox.valueChanged.connect(self.onLimitLenMaxChanged)
self.jForm.limitRotMinSpinbox.valueChanged.connect(self.onLimitRotMinChanged)
self.jForm.limitRotMaxSpinbox.valueChanged.connect(self.onLimitRotMaxChanged)
bind = Gui.ExpressionBinding(self.jForm.limitLenMinSpinbox).bind(self.joint, "LengthMin")
bind = Gui.ExpressionBinding(self.jForm.limitLenMaxSpinbox).bind(self.joint, "LengthMax")
bind = Gui.ExpressionBinding(self.jForm.limitRotMinSpinbox).bind(self.joint, "AngleMin")
bind = Gui.ExpressionBinding(self.jForm.limitRotMaxSpinbox).bind(self.joint, "AngleMax")
if self.creating:
# This has to be after adaptUi so that properties default values are adapted
@@ -1299,7 +1314,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.callbackMove = self.view.addEventCallback("SoLocation2Event", self.moveMouse)
self.callbackKey = self.view.addEventCallback("SoKeyboardEvent", self.KeyboardEvent)
self.form.featureList.installEventFilter(self)
self.jForm.featureList.installEventFilter(self)
self.addition_rejected = False
@@ -1393,7 +1408,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.updateJoint()
def createJointObject(self):
type_index = self.form.jointType.currentIndex()
type_index = self.jForm.jointType.currentIndex()
if self.activeType == "Part":
self.joint = self.assembly.newObject("App::FeaturePython", "Temporary joint")
@@ -1406,139 +1421,139 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
ViewProviderJoint(self.joint.ViewObject)
def onJointTypeChanged(self, index):
self.jType = JointTypes[self.form.jointType.currentIndex()]
self.jType = JointTypes[self.jForm.jointType.currentIndex()]
self.joint.Proxy.setJointType(self.joint, self.jType)
self.adaptUi()
def onDistanceChanged(self, quantity):
self.joint.Distance = self.form.distanceSpinbox.property("rawValue")
self.joint.Distance = self.jForm.distanceSpinbox.property("rawValue")
def onDistance2Changed(self, quantity):
self.joint.Distance2 = self.form.distanceSpinbox2.property("rawValue")
self.joint.Distance2 = self.jForm.distanceSpinbox2.property("rawValue")
def onOffsetChanged(self, quantity):
if self.blockOffsetRotation:
return
self.joint.Offset2.Base = App.Vector(0, 0, self.form.offsetSpinbox.property("rawValue"))
self.joint.Offset2.Base = App.Vector(0, 0, self.jForm.offsetSpinbox.property("rawValue"))
def onRotationChanged(self, quantity):
if self.blockOffsetRotation:
return
yaw = self.form.rotationSpinbox.property("rawValue")
yaw = self.jForm.rotationSpinbox.property("rawValue")
ypr = self.joint.Offset2.Rotation.getYawPitchRoll()
self.joint.Offset2.Rotation.setYawPitchRoll(yaw, ypr[1], ypr[2])
def onLimitLenMinChanged(self, quantity):
if self.form.limitCheckbox1.isChecked():
self.joint.LengthMin = self.form.limitLenMinSpinbox.property("rawValue")
if self.jForm.limitCheckbox1.isChecked():
self.joint.LengthMin = self.jForm.limitLenMinSpinbox.property("rawValue")
def onLimitLenMaxChanged(self, quantity):
if self.form.limitCheckbox2.isChecked():
self.joint.LengthMax = self.form.limitLenMaxSpinbox.property("rawValue")
if self.jForm.limitCheckbox2.isChecked():
self.joint.LengthMax = self.jForm.limitLenMaxSpinbox.property("rawValue")
def onLimitRotMinChanged(self, quantity):
if self.form.limitCheckbox3.isChecked():
self.joint.AngleMin = self.form.limitRotMinSpinbox.property("rawValue")
if self.jForm.limitCheckbox3.isChecked():
self.joint.AngleMin = self.jForm.limitRotMinSpinbox.property("rawValue")
def onLimitRotMaxChanged(self, quantity):
if self.form.limitCheckbox4.isChecked():
self.joint.AngleMax = self.form.limitRotMaxSpinbox.property("rawValue")
if self.jForm.limitCheckbox4.isChecked():
self.joint.AngleMax = self.jForm.limitRotMaxSpinbox.property("rawValue")
def onReverseClicked(self):
self.joint.Proxy.flipOnePart(self.joint)
def reverseRotToggled(self, val):
if val:
self.form.jointType.setCurrentIndex(JointTypes.index("Gears"))
self.jForm.jointType.setCurrentIndex(JointTypes.index("Gears"))
else:
self.form.jointType.setCurrentIndex(JointTypes.index("Belt"))
self.jForm.jointType.setCurrentIndex(JointTypes.index("Belt"))
def adaptUi(self):
jType = self.jType
needDistance = jType in JointUsingDistance
self.form.distanceLabel.setVisible(needDistance)
self.form.distanceSpinbox.setVisible(needDistance)
self.jForm.distanceLabel.setVisible(needDistance)
self.jForm.distanceSpinbox.setVisible(needDistance)
if needDistance:
if jType == "Distance":
self.form.distanceLabel.setText(translate("Assembly", "Distance"))
self.jForm.distanceLabel.setText(translate("Assembly", "Distance"))
elif jType == "Angle":
self.form.distanceLabel.setText(translate("Assembly", "Angle"))
self.jForm.distanceLabel.setText(translate("Assembly", "Angle"))
elif jType == "Gears" or jType == "Belt":
self.form.distanceLabel.setText(translate("Assembly", "Radius 1"))
self.jForm.distanceLabel.setText(translate("Assembly", "Radius 1"))
else:
self.form.distanceLabel.setText(translate("Assembly", "Pitch radius"))
self.jForm.distanceLabel.setText(translate("Assembly", "Pitch radius"))
if jType == "Angle":
self.form.distanceSpinbox.setProperty("unit", "deg")
self.jForm.distanceSpinbox.setProperty("unit", "deg")
else:
self.form.distanceSpinbox.setProperty("unit", "mm")
self.jForm.distanceSpinbox.setProperty("unit", "mm")
needDistance2 = jType in JointUsingDistance2
self.form.distanceLabel2.setVisible(needDistance2)
self.form.distanceSpinbox2.setVisible(needDistance2)
self.form.reverseRotCheckbox.setVisible(needDistance2)
self.jForm.distanceLabel2.setVisible(needDistance2)
self.jForm.distanceSpinbox2.setVisible(needDistance2)
self.jForm.reverseRotCheckbox.setVisible(needDistance2)
if jType in JointNoNegativeDistance:
# Setting minimum to 0.01 to prevent 0 and negative values
self.form.distanceSpinbox.setProperty("minimum", 1e-7)
if self.form.distanceSpinbox.property("rawValue") == 0.0:
self.form.distanceSpinbox.setProperty("rawValue", 1.0)
self.jForm.distanceSpinbox.setProperty("minimum", 1e-7)
if self.jForm.distanceSpinbox.property("rawValue") == 0.0:
self.jForm.distanceSpinbox.setProperty("rawValue", 1.0)
if jType == "Gears" or jType == "Belt":
self.form.distanceSpinbox2.setProperty("minimum", 1e-7)
if self.form.distanceSpinbox2.property("rawValue") == 0.0:
self.form.distanceSpinbox2.setProperty("rawValue", 1.0)
self.jForm.distanceSpinbox2.setProperty("minimum", 1e-7)
if self.jForm.distanceSpinbox2.property("rawValue") == 0.0:
self.jForm.distanceSpinbox2.setProperty("rawValue", 1.0)
else:
self.form.distanceSpinbox.setProperty("minimum", float("-inf"))
self.form.distanceSpinbox2.setProperty("minimum", float("-inf"))
self.jForm.distanceSpinbox.setProperty("minimum", float("-inf"))
self.jForm.distanceSpinbox2.setProperty("minimum", float("-inf"))
advancedOffset = self.form.advancedOffsetCheckbox.isChecked()
advancedOffset = self.jForm.advancedOffsetCheckbox.isChecked()
needOffset = jType in JointUsingOffset
needRotation = jType in JointUsingRotation
self.form.offset1Label.setVisible(advancedOffset)
self.form.offset2Label.setVisible(advancedOffset)
self.form.offset1Button.setVisible(advancedOffset)
self.form.offset2Button.setVisible(advancedOffset)
self.form.offsetLabel.setVisible(not advancedOffset and needOffset)
self.form.offsetSpinbox.setVisible(not advancedOffset and needOffset)
self.form.rotationLabel.setVisible(not advancedOffset and needRotation)
self.form.rotationSpinbox.setVisible(not advancedOffset and needRotation)
self.jForm.offset1Label.setVisible(advancedOffset)
self.jForm.offset2Label.setVisible(advancedOffset)
self.jForm.offset1Button.setVisible(advancedOffset)
self.jForm.offset2Button.setVisible(advancedOffset)
self.jForm.offsetLabel.setVisible(not advancedOffset and needOffset)
self.jForm.offsetSpinbox.setVisible(not advancedOffset and needOffset)
self.jForm.rotationLabel.setVisible(not advancedOffset and needRotation)
self.jForm.rotationSpinbox.setVisible(not advancedOffset and needRotation)
self.form.PushButtonReverse.setVisible(jType in JointUsingReverse)
self.jForm.PushButtonReverse.setVisible(jType in JointUsingReverse)
needLengthLimits = jType in JointUsingLimitLength
needAngleLimits = jType in JointUsingLimitAngle
needLimits = needLengthLimits or needAngleLimits
self.form.groupBox_limits.setVisible(needLimits)
self.jForm.groupBox_limits.setVisible(needLimits)
if needLimits:
self.joint.EnableLengthMin = self.form.limitCheckbox1.isChecked()
self.joint.EnableLengthMax = self.form.limitCheckbox2.isChecked()
self.joint.EnableAngleMin = self.form.limitCheckbox3.isChecked()
self.joint.EnableAngleMax = self.form.limitCheckbox4.isChecked()
self.joint.EnableLengthMin = self.jForm.limitCheckbox1.isChecked()
self.joint.EnableLengthMax = self.jForm.limitCheckbox2.isChecked()
self.joint.EnableAngleMin = self.jForm.limitCheckbox3.isChecked()
self.joint.EnableAngleMax = self.jForm.limitCheckbox4.isChecked()
self.form.limitCheckbox1.setVisible(needLengthLimits)
self.form.limitCheckbox2.setVisible(needLengthLimits)
self.form.limitLenMinSpinbox.setVisible(needLengthLimits)
self.form.limitLenMaxSpinbox.setVisible(needLengthLimits)
self.jForm.limitCheckbox1.setVisible(needLengthLimits)
self.jForm.limitCheckbox2.setVisible(needLengthLimits)
self.jForm.limitLenMinSpinbox.setVisible(needLengthLimits)
self.jForm.limitLenMaxSpinbox.setVisible(needLengthLimits)
self.form.limitCheckbox3.setVisible(needAngleLimits)
self.form.limitCheckbox4.setVisible(needAngleLimits)
self.form.limitRotMinSpinbox.setVisible(needAngleLimits)
self.form.limitRotMaxSpinbox.setVisible(needAngleLimits)
self.jForm.limitCheckbox3.setVisible(needAngleLimits)
self.jForm.limitCheckbox4.setVisible(needAngleLimits)
self.jForm.limitRotMinSpinbox.setVisible(needAngleLimits)
self.jForm.limitRotMaxSpinbox.setVisible(needAngleLimits)
if needLengthLimits:
self.form.limitLenMinSpinbox.setEnabled(self.joint.EnableLengthMin)
self.form.limitLenMaxSpinbox.setEnabled(self.joint.EnableLengthMax)
self.jForm.limitLenMinSpinbox.setEnabled(self.joint.EnableLengthMin)
self.jForm.limitLenMaxSpinbox.setEnabled(self.joint.EnableLengthMax)
self.onLimitLenMinChanged(0) # dummy value
self.onLimitLenMaxChanged(0)
if needAngleLimits:
self.form.limitRotMinSpinbox.setEnabled(self.joint.EnableAngleMin)
self.form.limitRotMaxSpinbox.setEnabled(self.joint.EnableAngleMax)
self.jForm.limitRotMinSpinbox.setEnabled(self.joint.EnableAngleMin)
self.jForm.limitRotMaxSpinbox.setEnabled(self.joint.EnableAngleMax)
self.onLimitRotMinChanged(0)
self.onLimitRotMaxChanged(0)
@@ -1547,14 +1562,14 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
def updateOffsetWidgets(self):
# Makes sure the values in both the simplified and advanced tabs are sync.
pos = self.joint.Offset1.Base
self.form.offset1Button.setText(f"({pos.x}, {pos.y}, {pos.z})")
self.jForm.offset1Button.setText(f"({pos.x}, {pos.y}, {pos.z})")
pos = self.joint.Offset2.Base
self.form.offset2Button.setText(f"({pos.x}, {pos.y}, {pos.z})")
self.jForm.offset2Button.setText(f"({pos.x}, {pos.y}, {pos.z})")
self.blockOffsetRotation = True
self.form.offsetSpinbox.setProperty("rawValue", pos.z)
self.form.rotationSpinbox.setProperty(
self.jForm.offsetSpinbox.setProperty("rawValue", pos.z)
self.jForm.rotationSpinbox.setProperty(
"rawValue", self.joint.Offset2.Rotation.getYawPitchRoll()[0]
)
self.blockOffsetRotation = False
@@ -1584,23 +1599,23 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
Gui.Selection.addSelection(ref1[0].Document.Name, ref1[0].Name, ref1[1][0])
Gui.Selection.addSelection(ref2[0].Document.Name, ref2[0].Name, ref2[1][0])
self.form.distanceSpinbox.setProperty("rawValue", self.joint.Distance)
self.form.distanceSpinbox2.setProperty("rawValue", self.joint.Distance2)
self.form.offsetSpinbox.setProperty("rawValue", self.joint.Offset2.Base.z)
self.form.rotationSpinbox.setProperty(
self.jForm.distanceSpinbox.setProperty("rawValue", self.joint.Distance)
self.jForm.distanceSpinbox2.setProperty("rawValue", self.joint.Distance2)
self.jForm.offsetSpinbox.setProperty("rawValue", self.joint.Offset2.Base.z)
self.jForm.rotationSpinbox.setProperty(
"rawValue", self.joint.Offset2.Rotation.getYawPitchRoll()[0]
)
self.form.limitCheckbox1.setChecked(self.joint.EnableLengthMin)
self.form.limitCheckbox2.setChecked(self.joint.EnableLengthMax)
self.form.limitCheckbox3.setChecked(self.joint.EnableAngleMin)
self.form.limitCheckbox4.setChecked(self.joint.EnableAngleMax)
self.form.limitLenMinSpinbox.setProperty("rawValue", self.joint.LengthMin)
self.form.limitLenMaxSpinbox.setProperty("rawValue", self.joint.LengthMax)
self.form.limitRotMinSpinbox.setProperty("rawValue", self.joint.AngleMin)
self.form.limitRotMaxSpinbox.setProperty("rawValue", self.joint.AngleMax)
self.jForm.limitCheckbox1.setChecked(self.joint.EnableLengthMin)
self.jForm.limitCheckbox2.setChecked(self.joint.EnableLengthMax)
self.jForm.limitCheckbox3.setChecked(self.joint.EnableAngleMin)
self.jForm.limitCheckbox4.setChecked(self.joint.EnableAngleMax)
self.jForm.limitLenMinSpinbox.setProperty("rawValue", self.joint.LengthMin)
self.jForm.limitLenMaxSpinbox.setProperty("rawValue", self.joint.LengthMax)
self.jForm.limitRotMinSpinbox.setProperty("rawValue", self.joint.AngleMin)
self.jForm.limitRotMaxSpinbox.setProperty("rawValue", self.joint.AngleMax)
self.form.jointType.setCurrentIndex(JointTypes.index(self.joint.JointType))
self.jForm.jointType.setCurrentIndex(JointTypes.index(self.joint.JointType))
self.updateJointList()
def updateJoint(self):
@@ -1611,7 +1626,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.joint.Proxy.setJointConnectors(self.joint, self.refs)
def updateJointList(self):
self.form.featureList.clear()
self.jForm.featureList.clear()
simplified_names = []
for ref in self.refs:
@@ -1621,7 +1636,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
if element_name != "":
sname = sname + "." + element_name
simplified_names.append(sname)
self.form.featureList.addItems(simplified_names)
self.jForm.featureList.addItems(simplified_names)
def updateLimits(self):
needLengthLimits = self.jType in JointUsingLimitLength
@@ -1629,28 +1644,28 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
if needLengthLimits:
distance = UtilsAssembly.getJointDistance(self.joint)
if (
not self.form.limitCheckbox1.isChecked()
and self.form.limitLenMinSpinbox.property("expression") == ""
not self.jForm.limitCheckbox1.isChecked()
and self.jForm.limitLenMinSpinbox.property("expression") == ""
):
self.form.limitLenMinSpinbox.setProperty("rawValue", distance)
self.jForm.limitLenMinSpinbox.setProperty("rawValue", distance)
if (
not self.form.limitCheckbox2.isChecked()
and self.form.limitLenMaxSpinbox.property("expression") == ""
not self.jForm.limitCheckbox2.isChecked()
and self.jForm.limitLenMaxSpinbox.property("expression") == ""
):
self.form.limitLenMaxSpinbox.setProperty("rawValue", distance)
self.jForm.limitLenMaxSpinbox.setProperty("rawValue", distance)
if needAngleLimits:
angle = UtilsAssembly.getJointXYAngle(self.joint) / math.pi * 180
if (
not self.form.limitCheckbox3.isChecked()
and self.form.limitRotMinSpinbox.property("expression") == ""
not self.jForm.limitCheckbox3.isChecked()
and self.jForm.limitRotMinSpinbox.property("expression") == ""
):
self.form.limitRotMinSpinbox.setProperty("rawValue", angle)
self.jForm.limitRotMinSpinbox.setProperty("rawValue", angle)
if (
not self.form.limitCheckbox4.isChecked()
and self.form.limitRotMaxSpinbox.property("expression") == ""
not self.jForm.limitCheckbox4.isChecked()
and self.jForm.limitRotMaxSpinbox.property("expression") == ""
):
self.form.limitRotMaxSpinbox.setProperty("rawValue", angle)
self.jForm.limitRotMaxSpinbox.setProperty("rawValue", angle)
def moveMouse(self, info):
if len(self.refs) >= 2 or (
@@ -1700,7 +1715,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
self.accept()
def eventFilter(self, watched, event):
if self.form is not None and watched == self.form.featureList:
if self.jForm is not None and watched == self.jForm.featureList:
if event.type() == QtCore.QEvent.ShortcutOverride:
if event.key() == QtCore.Qt.Key_Delete:
event.accept() # Accept the event only if the key is Delete
@@ -1709,7 +1724,7 @@ class TaskAssemblyCreateJoint(QtCore.QObject):
elif event.type() == QtCore.QEvent.KeyPress:
if event.key() == QtCore.Qt.Key_Delete:
selected_indexes = self.form.featureList.selectedIndexes()
selected_indexes = self.jForm.featureList.selectedIndexes()
for index in selected_indexes:
row = index.row()

View File

@@ -1223,6 +1223,24 @@ def addVertexToReference(ref, vertex_name):
return ref
def createPart(partName, doc):
if not doc:
raise ValueError("No active document to add a part to.")
part = doc.addObject("App::Part", partName)
body = part.newObject("PartDesign::Body", "Body")
# Gui.ActiveDocument.ActiveView.setActiveObject('pdbody', body)
sketch = body.newObject("Sketcher::SketchObject", "Sketch")
sketch.MapMode = "FlatFace"
sketch.AttachmentSupport = [(body.Origin.OriginFeatures[3], "")] # XY_Plane
# add a circle as a base shape for visualisation
sketch.addGeometry(Part.Circle(App.Vector(0, 0), App.Vector(0, 0, 1), 5), False)
doc.recompute()
return part, body
def getLinkGroup(linkElement):
if linkElement.TypeId == "App::LinkElement":
for obj in linkElement.InList: