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:
@@ -9,6 +9,7 @@ set(Assembly_Scripts
|
||||
CommandCreateAssembly.py
|
||||
CommandCreateBom.py
|
||||
CommandInsertLink.py
|
||||
CommandInsertNewPart.py
|
||||
CommandSolveAssembly.py
|
||||
CommandCreateJoint.py
|
||||
CommandCreateView.py
|
||||
|
||||
@@ -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())
|
||||
|
||||
194
src/Mod/Assembly/CommandInsertNewPart.py
Normal file
194
src/Mod/Assembly/CommandInsertNewPart.py
Normal 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())
|
||||
@@ -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",
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user