diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index 23c91b2f57..c82bb15bcc 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -168,6 +168,11 @@ SET(PathScripts_post_SRCS PathScripts/post/uccnc_post.py ) +SET(PathPythonGui_SRCS + PathPythonGui/__init__.py + PathPythonGui/simple_edit_panel.py +) + SET(Tools_SRCS Tools/README.md ) @@ -266,6 +271,7 @@ SET(all_files ${PathScripts_SRCS} ${Generator_SRCS} ${PathScripts_post_SRCS} + ${PathPythonGui_SRCS} ${Tools_SRCS} ${Tools_Bit_SRCS} ${Tools_Library_SRCS} @@ -326,6 +332,13 @@ INSTALL( Mod/Path/PathScripts/post ) +INSTALL( + FILES + ${PathPythonGui_SRCS} + DESTINATION + Mod/Path/PathPythonGui +) + INSTALL( FILES ${Tools_SRCS} diff --git a/src/Mod/Path/Gui/Resources/panels/DressUpLeadInOutEdit.ui b/src/Mod/Path/Gui/Resources/panels/DressUpLeadInOutEdit.ui index 53710a2ad7..0682699e3c 100644 --- a/src/Mod/Path/Gui/Resources/panels/DressUpLeadInOutEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/DressUpLeadInOutEdit.ui @@ -1,7 +1,7 @@ - TaskPanel - + Path_DressupLeadInOut + 0 @@ -11,7 +11,7 @@ - LeadInOut + LeadInOut @@ -36,8 +36,8 @@ 0 0 - 363 - 403 + 365 + 406 @@ -142,21 +142,6 @@ 16777215 - - - Arc - - - - - Tangent - - - - - Perpendicular - - @@ -237,21 +222,6 @@ 16777215 - - - Arc - - - - - Tangent - - - - - Perpendicular - - @@ -314,16 +284,6 @@ 16777215 - - - Radius - - - - - Center - - diff --git a/src/Mod/Path/PathPythonGui/__init__.py b/src/Mod/Path/PathPythonGui/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Mod/Path/PathPythonGui/simple_edit_panel.py b/src/Mod/Path/PathPythonGui/simple_edit_panel.py new file mode 100644 index 0000000000..402415d4dd --- /dev/null +++ b/src/Mod/Path/PathPythonGui/simple_edit_panel.py @@ -0,0 +1,134 @@ +from __future__ import print_function + +import FreeCAD +import FreeCADGui +from PySide import QtGui + +translate = FreeCAD.Qt.translate + +PROP_TYPE_QTYES = ["App::PropertyDistance", "App::PropertyAngle"] +PROP_TYPE_NUMERIC = PROP_TYPE_QTYES + ["App::PropertyPercent", "App:PropertyFloat"] + + +class SimpleEditPanel: + """A simple property edit panel for a FreeCAD object + + Attributes: + _transaction_name: Name to use for the undo transaction used in this panel. + _ui_file: Path to the Qt UI file to use when creating this panel. Must be + overridden by the subclass. + """ + + _transaction_name = "Property Edit" + _ui_file = "" + + def __init__(self, obj, view): + self.obj = obj + self.viewProvider = view + FreeCAD.ActiveDocument.openTransaction(self._transaction_name) + self.form = FreeCADGui.PySideUic.loadUi(self._ui_file) + self._fc = {} + self.setupUi() + + def getFields(self): + for prop_name, (get_field, set_field) in self._fc.items(): + setattr(self.obj, prop_name, get_field()) + + def setFields(self): + for prop_name, (get_field, set_field) in self._fc.items(): + set_field(getattr(self.obj, prop_name)) + + def connectWidget(self, prop_name, widget, custom_lbls=None): + """Connect a widget to a proxy object property + + This registers a connection between a UI widget and an object property, + performing a series of convenience functions in the process, namely: + * It copies the tooltip from the object property, with proper handling + of translation. + * If it is an enum property, it loads the translated choices from the + property into the widget. Optionally the caller can override some or + all of these labels using the `custom_lbls` dictionary. + * It registers type conversion functions so when the form is applied or + accepted the data is converted and stored/retrieved without manual + intervention. + """ + if custom_lbls is None: + custom_lbls = {} + prop_type = self.obj.getTypeIdOfProperty(prop_name) + widget_type = type(widget).__name__ + if prop_type == "App::PropertyEnumeration" and widget_type == "QComboBox": + enum = self.obj.getEnumerationsOfProperty(prop_name) + # Populate the combo box with the enumeration elements, use the form context for translation + elements = [ + translate(self.form.objectName(), custom_lbls.get(itm, itm)) + for itm in enum + ] + widget.clear() + widget.addItems(elements) + + def _getter(): + return enum[widget.currentIndex()] + + def _setter(val): + widget.setCurrentIndex(enum.index(val)) + + self._fc[prop_name] = _getter, _setter + elif prop_type == "App::PropertyBool" and widget_type == "QCheckBox": + self._fc[prop_name] = widget.isChecked, widget.setChecked + elif prop_type in PROP_TYPE_NUMERIC and widget_type == "QDoubleSpinBox": + self._fc[prop_name] = widget.value, widget.setValue + elif prop_type in PROP_TYPE_QTYES and widget_type == "QLineEdit": + self._fc[prop_name] = widget.text, lambda v: widget.setText(str(v)) + else: + raise ValueError( + f"Unsupported connection between '{prop_type}' property and '{widget_type}' widget" + ) + # Set the tooltip to the one corresponding to the property. + widget.setToolTip( + translate("App::Property", self.obj.getDocumentationOfProperty(prop_name)) + ) + + def getStandardButtons(self): + return int( + QtGui.QDialogButtonBox.Ok + | QtGui.QDialogButtonBox.Apply + | QtGui.QDialogButtonBox.Cancel + ) + + def clicked(self, button): + # callback for standard buttons + if button == QtGui.QDialogButtonBox.Apply: + self.updateModel() + FreeCAD.ActiveDocument.recompute() + if button == QtGui.QDialogButtonBox.Cancel: + self.abort() + + def abort(self): + FreeCAD.ActiveDocument.abortTransaction() + self.cleanup(True) + + def reject(self): + FreeCAD.ActiveDocument.abortTransaction() + FreeCADGui.Control.closeDialog() + FreeCAD.ActiveDocument.recompute() + + def accept(self): + self.getFields() + FreeCAD.ActiveDocument.commitTransaction() + FreeCADGui.ActiveDocument.resetEdit() + FreeCADGui.Control.closeDialog() + FreeCAD.ActiveDocument.recompute() + + def cleanup(self, gui): + self.viewProvider.clearTaskPanel() + if gui: + FreeCADGui.Control.closeDialog() + FreeCAD.ActiveDocument.recompute() + + def updateModel(self): + self.getFields() + self.obj.Proxy.execute(self.obj) + FreeCAD.ActiveDocument.recompute() + + def open(self): + pass diff --git a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py index e9435d66fc..ceb9dd6838 100644 --- a/src/Mod/Path/PathScripts/PathDressupLeadInOut.py +++ b/src/Mod/Path/PathScripts/PathDressupLeadInOut.py @@ -33,46 +33,127 @@ import PathScripts.PathUtils as PathUtils import math import copy -from PySide import QtCore, QtGui - __doc__ = """LeadInOut Dressup MASHIN-CRC USE ROLL-ON ROLL-OFF to profile""" -if FreeCAD.GuiUp: - import FreeCADGui +from PySide.QtCore import QT_TRANSLATE_NOOP -# Qt translation handling -def translate(text, context="Path_DressupLeadInOut", disambig=None): - return QtCore.QCoreApplication.translate(context, text, disambig) +from PathPythonGui.simple_edit_panel import SimpleEditPanel -PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) +translate = FreeCAD.Qt.translate -movecommands = ['G1', 'G01', 'G2', 'G02', 'G3', 'G03'] -rapidcommands = ['G0', 'G00'] -arccommands = ['G2', 'G3', 'G02', 'G03'] +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) + + +movecommands = ["G1", "G01", "G2", "G02", "G3", "G03"] +rapidcommands = ["G0", "G00"] +arccommands = ["G2", "G3", "G02", "G03"] currLocation = {} class ObjectDressup: - def __init__(self, obj): + lead_styles = [ + QT_TRANSLATE_NOOP("Path_DressupLeadInOut", "Arc"), + QT_TRANSLATE_NOOP("Path_DressupLeadInOut", "Tangent"), + QT_TRANSLATE_NOOP("Path_DressupLeadInOut", "Perpendicular"), + ] self.obj = obj - obj.addProperty("App::PropertyLink", "Base", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "The base path to modify")) - obj.addProperty("App::PropertyBool", "LeadIn", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Calculate roll-on to path")) - obj.addProperty("App::PropertyBool", "LeadOut", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Calculate roll-off from path")) - obj.addProperty("App::PropertyBool", "KeepToolDown", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Keep the Tool Down in Path")) - obj.addProperty("App::PropertyBool", "UseMachineCRC", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Use Machine Cutter Radius Compensation /Tool Path Offset G41/G42")) - obj.addProperty("App::PropertyDistance", "Length", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Length or Radius of the approach")) - obj.addProperty("App::PropertyEnumeration", "StyleOn", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupLeadInOut", "The Style of LeadIn the Path")) - obj.StyleOn = ["Arc", "Tangent", "Perpendicular"] - obj.addProperty("App::PropertyEnumeration", "StyleOff", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupLeadInOut", "The Style of LeadOut the Path")) - obj.StyleOff = ["Arc", "Tangent", "Perpendicular"] - obj.addProperty("App::PropertyEnumeration", "RadiusCenter", "Path", QtCore.QT_TRANSLATE_NOOP("Path_DressupLeadInOut", "The Mode of Point Radiusoffset or Center")) - obj.RadiusCenter = ["Radius", "Center"] + obj.addProperty( + "App::PropertyLink", + "Base", + "Path", + QT_TRANSLATE_NOOP("App::Property", "The base path to modify"), + ) + obj.addProperty( + "App::PropertyBool", + "LeadIn", + "Path", + QT_TRANSLATE_NOOP("App::Property", "Calculate roll-on to path"), + ) + obj.addProperty( + "App::PropertyBool", + "LeadOut", + "Path", + QT_TRANSLATE_NOOP("App::Property", "Calculate roll-off from path"), + ) + obj.addProperty( + "App::PropertyBool", + "KeepToolDown", + "Path", + QT_TRANSLATE_NOOP("App::Property", "Keep the Tool Down in Path"), + ) + obj.addProperty( + "App::PropertyBool", + "UseMachineCRC", + "Path", + QT_TRANSLATE_NOOP( + "App::Property", + "Use Machine Cutter Radius Compensation /Tool Path Offset G41/G42", + ), + ) + obj.addProperty( + "App::PropertyDistance", + "Length", + "Path", + QT_TRANSLATE_NOOP("App::Property", "Length or Radius of the approach"), + ) + obj.addProperty( + "App::PropertyEnumeration", + "StyleOn", + "Path", + QT_TRANSLATE_NOOP("App::Property", "The Style of motion into the Path"), + ) + obj.StyleOn = lead_styles + obj.addProperty( + "App::PropertyEnumeration", + "StyleOff", + "Path", + QT_TRANSLATE_NOOP("App::Property", "The Style of motion out of the Path"), + ) + obj.StyleOff = lead_styles + obj.addProperty( + "App::PropertyEnumeration", + "RadiusCenter", + "Path", + QT_TRANSLATE_NOOP( + "App::Property", "The Mode of Point Radiusoffset or Center" + ), + ) + obj.RadiusCenter = [ + QT_TRANSLATE_NOOP("Path_DressupLeadInOut", "Radius"), + QT_TRANSLATE_NOOP("Path_DressupLeadInOut", "Center"), + ] obj.Proxy = self - obj.addProperty("App::PropertyDistance", "ExtendLeadIn", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extends LeadIn distance")) - obj.addProperty("App::PropertyDistance", "ExtendLeadOut", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extends LeadOut distance")) - obj.addProperty("App::PropertyBool", "RapidPlunge", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Perform plunges with G0")) - obj.addProperty("App::PropertyBool", "IncludeLayers", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Apply LeadInOut to layers within an operation")) + obj.addProperty( + "App::PropertyDistance", + "ExtendLeadIn", + "Path", + QT_TRANSLATE_NOOP("App::Property", "Extends LeadIn distance"), + ) + obj.addProperty( + "App::PropertyDistance", + "ExtendLeadOut", + "Path", + QT_TRANSLATE_NOOP("App::Property", "Extends LeadOut distance"), + ) + obj.addProperty( + "App::PropertyBool", + "RapidPlunge", + "Path", + QT_TRANSLATE_NOOP("App::Property", "Perform plunges with G0"), + ) + obj.addProperty( + "App::PropertyBool", + "IncludeLayers", + "Path", + QT_TRANSLATE_NOOP( + "App::Property", "Apply LeadInOut to layers within an operation" + ), + ) self.wire = None self.rapids = None @@ -90,9 +171,9 @@ class ObjectDressup: obj.LeadOut = True obj.KeepToolDown = False obj.UseMachineCRC = False - obj.StyleOn = 'Arc' - obj.StyleOff = 'Arc' - obj.RadiusCenter = 'Radius' + obj.StyleOn = "Arc" + obj.StyleOff = "Arc" + obj.RadiusCenter = "Radius" obj.ExtendLeadIn = 0 obj.ExtendLeadOut = 0 obj.RapidPlunge = False @@ -106,7 +187,10 @@ class ObjectDressup: if not obj.Base.Path: return if obj.Length < 0: - PathLog.error(translate("Length/Radius positive not Null")+"\n") + PathLog.error( + translate("Path_DressupLeadInOut", "Length/Radius positive not Null") + + "\n" + ) obj.Length = 0.1 self.wire, self.rapids = PathGeom.wireForPath(obj.Base.Path) obj.Path = self.generateLeadInOutCurve(obj) @@ -114,22 +198,22 @@ class ObjectDressup: def getDirectionOfPath(self, obj): op = PathDressup.baseOp(obj.Base) - if hasattr(op, 'Side') and op.Side == 'Outside': - if hasattr(op, 'Direction') and op.Direction == 'CW': - return 'left' + if hasattr(op, "Side") and op.Side == "Outside": + if hasattr(op, "Direction") and op.Direction == "CW": + return "left" else: - return 'right' + return "right" else: - if hasattr(op, 'Direction') and op.Direction == 'CW': - return 'right' - return 'left' + if hasattr(op, "Direction") and op.Direction == "CW": + return "right" + return "left" - def getSideOfPath(self, obj): + def getSideOfPath(self, obj): op = PathDressup.baseOp(obj.Base) - if hasattr(op, 'Side'): + if hasattr(op, "Side"): return op.Side - return '' + return "" def normalize(self, Vector): vx = 0 @@ -137,34 +221,34 @@ class ObjectDressup: x = Vector.x y = Vector.y - length = math.sqrt(x*x + y*y) - if((math.fabs(length)) > 0.0000000000001): + length = math.sqrt(x * x + y * y) + if (math.fabs(length)) > 0.0000000000001: vx = round(x / length, 3) vy = round(y / length, 3) - + return FreeCAD.Vector(vx, vy, 0) - def invert(self, Vector): + def invert(self, Vector): x = Vector.x * -1 y = Vector.y * -1 z = Vector.z * -1 return FreeCAD.Vector(x, y, z) - def multiply(self, Vector, len): + def multiply(self, Vector, len): x = Vector.x * len y = Vector.y * len z = Vector.z * len return FreeCAD.Vector(x, y, z) - def rotate(self, Vector, angle): + def rotate(self, Vector, angle): s = math.sin(math.radians(angle)) c = math.cos(math.radians(angle)) - xnew = Vector.x * c - Vector.y * s; - ynew = Vector.x * s + Vector.y * c; + xnew = Vector.x * c - Vector.y * s + ynew = Vector.x * s + Vector.y * c return FreeCAD.Vector(xnew, ynew, Vector.z) def getLeadStart(self, obj, queue, action): - '''returns Lead In G-code.''' + """returns Lead In G-code.""" results = [] op = PathDressup.baseOp(obj.Base) tc = PathDressup.toolController(obj.Base) @@ -174,7 +258,7 @@ class ObjectDressup: arcs_identical = False # Set the correct twist command - if self.getDirectionOfPath(obj) == 'left': + if self.getDirectionOfPath(obj) == "left": arcdir = "G3" else: arcdir = "G2" @@ -192,10 +276,10 @@ class ObjectDressup: # PathLog.debug(" CURRENT_IN ARC : P0 X:{} Y:{} P1 X:{} Y:{} ".format(p0.x,p0.y,p1.x,p1.y)) # Calculate offset vector (will be overwritten for arcs) - if self.getDirectionOfPath(obj) == 'right': - off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) + if self.getDirectionOfPath(obj) == "right": + off_v = FreeCAD.Vector(v.y * R, -v.x * R, 0.0) else: - off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0) + off_v = FreeCAD.Vector(-v.y * R, v.x * R, 0.0) # Check if we enter at line or arc command if queue[1].Name in movecommands and queue[1].Name not in arccommands: @@ -203,14 +287,14 @@ class ObjectDressup: vec = p1.sub(p0) vec_n = self.normalize(vec) vec_inv = self.invert(vec_n) - vec_off = self.multiply(vec_inv, obj.ExtendLeadIn) - #PathLog.debug("LineCMD: {}, Vxinv: {}, Vyinv: {}, Vxoff: {}, Vyoff: {}".format(queue[0].Name, vec_inv.x, vec_inv.y, vec_off.x, vec_off.y)) + vec_off = self.multiply(vec_inv, obj.ExtendLeadIn) + # PathLog.debug("LineCMD: {}, Vxinv: {}, Vyinv: {}, Vxoff: {}, Vyoff: {}".format(queue[0].Name, vec_inv.x, vec_inv.y, vec_off.x, vec_off.y)) else: # We have an arc move # Calculate coordinates for middle of circle pij = copy.deepcopy(p0) - pij.x += queue[1].Parameters['I'] - pij.y += queue[1].Parameters['J'] + pij.x += queue[1].Parameters["I"] + pij.y += queue[1].Parameters["J"] # Check if lead in and operation go in same direction (usually for inner circles) if arcdir == queue[1].Name: @@ -221,9 +305,9 @@ class ObjectDressup: # Rotate vector to get direction for lead in if arcdir == "G2": - vec_rot = self.rotate(vec_circ, 90) + vec_rot = self.rotate(vec_circ, 90) else: - vec_rot = self.rotate(vec_circ, -90) + vec_rot = self.rotate(vec_circ, -90) # Normalize and invert vector vec_n = self.normalize(vec_rot) @@ -232,80 +316,103 @@ class ObjectDressup: # Calculate offset of lead in if arcdir == "G3": - off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0) + off_v = FreeCAD.Vector(-v.y * R, v.x * R, 0.0) else: - off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) + off_v = FreeCAD.Vector(v.y * R, -v.x * R, 0.0) # Multiply offset by LeadIn length - vec_off = self.multiply(vec_n, obj.ExtendLeadIn) + vec_off = self.multiply(vec_n, obj.ExtendLeadIn) - offsetvector = FreeCAD.Vector(v.x*R-vec_off.x, v.y*R-vec_off.y, 0) # IJ + offsetvector = FreeCAD.Vector(v.x * R - vec_off.x, v.y * R - vec_off.y, 0) # IJ - if obj.RadiusCenter == 'Radius': + if obj.RadiusCenter == "Radius": leadstart = (p0.add(off_v)).sub(offsetvector) # Rmode if arcs_identical: t = p0.sub(leadstart) t = p0.add(t) leadstart = t - offsetvector = self.multiply(offsetvector, -1) + offsetvector = self.multiply(offsetvector, -1) else: leadstart = p0.add(off_v) # Dmode - if action == 'start': - #extendcommand = Path.Command('G0', {"X": 0.0, "Y": 0.0, "Z": op.ClearanceHeight.Value}) - #results.append(extendcommand) - extendcommand = Path.Command('G0', {"X": leadstart.x, "Y": leadstart.y, "Z": op.ClearanceHeight.Value}) + if action == "start": + # extendcommand = Path.Command('G0', {"X": 0.0, "Y": 0.0, "Z": op.ClearanceHeight.Value}) + # results.append(extendcommand) + extendcommand = Path.Command( + "G0", + {"X": leadstart.x, "Y": leadstart.y, "Z": op.ClearanceHeight.Value}, + ) results.append(extendcommand) - extendcommand = Path.Command('G0', {"Z": op.SafeHeight.Value}) + extendcommand = Path.Command("G0", {"Z": op.SafeHeight.Value}) results.append(extendcommand) - if action == 'layer': + if action == "layer": if not obj.KeepToolDown: - extendcommand = Path.Command('G0', {"Z": op.SafeHeight.Value}) + extendcommand = Path.Command("G0", {"Z": op.SafeHeight.Value}) results.append(extendcommand) - extendcommand = Path.Command('G0', {"X": leadstart.x, "Y": leadstart.y}) + extendcommand = Path.Command("G0", {"X": leadstart.x, "Y": leadstart.y}) results.append(extendcommand) if not obj.RapidPlunge: - extendcommand = Path.Command('G1', {"X": leadstart.x, "Y": leadstart.y, "Z": p1.z, "F": vertFeed}) + extendcommand = Path.Command( + "G1", {"X": leadstart.x, "Y": leadstart.y, "Z": p1.z, "F": vertFeed} + ) else: - extendcommand = Path.Command('G0', {"X": leadstart.x, "Y": leadstart.y, "Z": p1.z,}) + extendcommand = Path.Command( + "G0", + { + "X": leadstart.x, + "Y": leadstart.y, + "Z": p1.z, + }, + ) results.append(extendcommand) if obj.UseMachineCRC: - if self.getDirectionOfPath(obj) == 'right': - results.append(Path.Command('G42', {'D': toolnummer})) + if self.getDirectionOfPath(obj) == "right": + results.append(Path.Command("G42", {"D": toolnummer})) else: - results.append(Path.Command('G41', {'D': toolnummer})) + results.append(Path.Command("G41", {"D": toolnummer})) - if obj.StyleOn == 'Arc': - arcmove = Path.Command(arcdir, {"X": p0.x+vec_off.x, "Y": p0.y+vec_off.y, "I": offsetvector.x+vec_off.x, "J": offsetvector.y+vec_off.y, "F": horizFeed}) # add G2/G3 move + if obj.StyleOn == "Arc": + arcmove = Path.Command( + arcdir, + { + "X": p0.x + vec_off.x, + "Y": p0.y + vec_off.y, + "I": offsetvector.x + vec_off.x, + "J": offsetvector.y + vec_off.y, + "F": horizFeed, + }, + ) # add G2/G3 move results.append(arcmove) if obj.ExtendLeadIn != 0: - extendcommand = Path.Command('G1', {"X": p0.x, "Y": p0.y, "F": horizFeed}) + extendcommand = Path.Command( + "G1", {"X": p0.x, "Y": p0.y, "F": horizFeed} + ) results.append(extendcommand) - elif obj.StyleOn == 'Tangent': - extendcommand = Path.Command('G1', {"X": p0.x, "Y": p0.y, "F": horizFeed}) + elif obj.StyleOn == "Tangent": + extendcommand = Path.Command("G1", {"X": p0.x, "Y": p0.y, "F": horizFeed}) results.append(extendcommand) else: PathLog.debug(" CURRENT_IN Perp") currLocation.update(results[-1].Parameters) - currLocation['Z'] = p1.z + currLocation["Z"] = p1.z return results def getLeadEnd(self, obj, queue, action): - '''returns the Gcode of LeadOut.''' - # pylint: disable=unused-argument + """returns the Gcode of LeadOut.""" + # pylint: disable=unused-argument results = [] horizFeed = PathDressup.toolController(obj.Base).HorizFeed.Value R = obj.Length.Value # Radius of roll or length arcs_identical = False # Set the correct twist command - if self.getDirectionOfPath(obj) == 'right': + if self.getDirectionOfPath(obj) == "right": arcdir = "G2" else: arcdir = "G3" @@ -319,10 +426,10 @@ class ObjectDressup: p1 = queue[1].Placement.Base v = self.normalize(p1.sub(p0)) - if self.getDirectionOfPath(obj) == 'right': - off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) + if self.getDirectionOfPath(obj) == "right": + off_v = FreeCAD.Vector(v.y * R, -v.x * R, 0.0) else: - off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0) + off_v = FreeCAD.Vector(-v.y * R, v.x * R, 0.0) # Check if we leave at line or arc command if queue[1].Name in movecommands and queue[1].Name not in arccommands: @@ -330,79 +437,86 @@ class ObjectDressup: vec = p1.sub(p0) vec_n = self.normalize(vec) vec_inv = self.invert(vec_n) - vec_off = self.multiply(vec_inv, obj.ExtendLeadOut) - #PathLog.debug("LineCMD: {}, Vxinv: {}, Vyinv: {}, Vxoff: {}, Vyoff: {}".format(queue[0].Name, vec_inv.x, vec_inv.y, vec_off.x, vec_off.y)) + vec_off = self.multiply(vec_inv, obj.ExtendLeadOut) + # PathLog.debug("LineCMD: {}, Vxinv: {}, Vyinv: {}, Vxoff: {}, Vyoff: {}".format(queue[0].Name, vec_inv.x, vec_inv.y, vec_off.x, vec_off.y)) else: # We have an arc move pij = copy.deepcopy(p0) - pij.x += queue[1].Parameters['I'] - pij.y += queue[1].Parameters['J'] + pij.x += queue[1].Parameters["I"] + pij.y += queue[1].Parameters["J"] ve = pij.sub(p1) if arcdir == queue[1].Name: arcs_identical = True if arcdir == "G2": - vec_rot = self.rotate(ve, -90) + vec_rot = self.rotate(ve, -90) else: - vec_rot = self.rotate(ve, 90) + vec_rot = self.rotate(ve, 90) vec_n = self.normalize(vec_rot) v = vec_n if arcdir == "G3": - off_v = FreeCAD.Vector(-v.y*R, v.x*R, 0.0) + off_v = FreeCAD.Vector(-v.y * R, v.x * R, 0.0) else: - off_v = FreeCAD.Vector(v.y*R, -v.x*R, 0.0) + off_v = FreeCAD.Vector(v.y * R, -v.x * R, 0.0) vec_inv = self.invert(vec_rot) - vec_off = self.multiply(vec_inv, obj.ExtendLeadOut) + vec_off = self.multiply(vec_inv, obj.ExtendLeadOut) - offsetvector = FreeCAD.Vector(v.x*R-vec_off.x, v.y*R-vec_off.y, 0.0) - if obj.RadiusCenter == 'Radius': + offsetvector = FreeCAD.Vector(v.x * R - vec_off.x, v.y * R - vec_off.y, 0.0) + if obj.RadiusCenter == "Radius": leadend = (p1.add(off_v)).add(offsetvector) # Rmode if arcs_identical: t = p1.sub(leadend) t = p1.add(t) leadend = t - off_v = self.multiply(off_v, -1) + off_v = self.multiply(off_v, -1) else: leadend = p1.add(off_v) # Dmode IJ = off_v # .negative() - #results.append(queue[1]) - if obj.StyleOff == 'Arc': + # results.append(queue[1]) + if obj.StyleOff == "Arc": if obj.ExtendLeadOut != 0: - extendcommand = Path.Command('G1', {"X": p1.x-vec_off.x, "Y": p1.y-vec_off.y, "F": horizFeed}) + extendcommand = Path.Command( + "G1", {"X": p1.x - vec_off.x, "Y": p1.y - vec_off.y, "F": horizFeed} + ) results.append(extendcommand) - arcmove = Path.Command(arcdir, {"X": leadend.x, "Y": leadend.y, "I": IJ.x, "J": IJ.y, "F": horizFeed}) # add G2/G3 move + arcmove = Path.Command( + arcdir, + {"X": leadend.x, "Y": leadend.y, "I": IJ.x, "J": IJ.y, "F": horizFeed}, + ) # add G2/G3 move results.append(arcmove) - elif obj.StyleOff == 'Tangent': - extendcommand = Path.Command('G1', {"X": leadend.x, "Y": leadend.y, "F": horizFeed}) + elif obj.StyleOff == "Tangent": + extendcommand = Path.Command( + "G1", {"X": leadend.x, "Y": leadend.y, "F": horizFeed} + ) results.append(extendcommand) else: PathLog.debug(" CURRENT_IN Perp") if obj.UseMachineCRC: # crc off - results.append(Path.Command('G40', {})) + results.append(Path.Command("G40", {})) return results def generateLeadInOutCurve(self, obj): - global currLocation # pylint: disable=global-statement + global currLocation # pylint: disable=global-statement firstmove = Path.Command("G0", {"X": 0, "Y": 0, "Z": 0}) op = PathDressup.baseOp(obj.Base) currLocation.update(firstmove.Parameters) newpath = [] queue = [] - action = 'start' - prevCmd = '' + action = "start" + prevCmd = "" layers = [] # Read in all commands for curCommand in obj.Base.Path.Commands: - #PathLog.debug("CurCMD: {}".format(curCommand)) + # PathLog.debug("CurCMD: {}".format(curCommand)) if curCommand.Name not in movecommands + rapidcommands: # Don't worry about non-move commands, just add to output newpath.append(curCommand) @@ -415,13 +529,21 @@ class ObjectDressup: continue if curCommand.Name in movecommands: - if prevCmd.Name in rapidcommands and curCommand.Name in movecommands and len(queue) > 0: + if ( + prevCmd.Name in rapidcommands + and curCommand.Name in movecommands + and len(queue) > 0 + ): # Layer changed: Save current layer cmds and prepare next layer layers.append(queue) queue = [] - if obj.IncludeLayers and curCommand.z < currLocation['Z'] and prevCmd.Name in movecommands: + if ( + obj.IncludeLayers + and curCommand.z < currLocation["Z"] + and prevCmd.Name in movecommands + ): # Layer change within move cmds - #PathLog.debug("Layer change in move: {}->{}".format(currLocation['Z'], curCommand.z)) + # PathLog.debug("Layer change in move: {}->{}".format(currLocation['Z'], curCommand.z)) layers.append(queue) queue = [] @@ -438,16 +560,16 @@ class ObjectDressup: # Go through each layer and add leadIn/Out idx = 0 for layer in layers: - #PathLog.debug("Layer {}".format(idx)) + # PathLog.debug("Layer {}".format(idx)) if obj.LeadIn: temp = self.getLeadStart(obj, layer, action) newpath.extend(temp) for cmd in layer: - #PathLog.debug("CurLoc: {}, NewCmd: {}".format(currLocation, cmd)) - #if currLocation['X'] == cmd.x and currLocation['Y'] == cmd.y and currLocation['Z'] == cmd.z and cmd.Name in ['G1', 'G01']: - #continue + # PathLog.debug("CurLoc: {}, NewCmd: {}".format(currLocation, cmd)) + # if currLocation['X'] == cmd.x and currLocation['Y'] == cmd.y and currLocation['Z'] == cmd.z and cmd.Name in ['G1', 'G01']: + # continue newpath.append(cmd) if obj.LeadOut: @@ -457,11 +579,11 @@ class ObjectDressup: temp = self.getLeadEnd(obj, tmp, action) newpath.extend(temp) - if not obj.KeepToolDown or idx == len(layers)-1: - extendcommand = Path.Command('G0', {"Z": op.ClearanceHeight.Value}) + if not obj.KeepToolDown or idx == len(layers) - 1: + extendcommand = Path.Command("G0", {"Z": op.ClearanceHeight.Value}) newpath.append(extendcommand) else: - action = 'layer' + action = "layer" idx += 1 @@ -469,112 +591,23 @@ class ObjectDressup: return Path.Path(commands) -class TaskPanel: - def __init__(self, obj, view): - self.obj = obj - self.viewProvider = view - self.form = FreeCADGui.PySideUic.loadUi(":/panels/DressUpLeadInOutEdit.ui") - self.setupUi() - - FreeCAD.ActiveDocument.openTransaction(translate("Path_DressupLeadInOut", "Edit LeadInOut Dress-up")) - - def getStandardButtons(self): - return int(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Cancel) - - def modifyStandardButtons(self, buttonBox): - self.buttonBox = buttonBox - - def setDirty(self): - self.isDirty = True - self.buttonBox.button(QtGui.QDialogButtonBox.Apply).setEnabled(True) - - def setClean(self): - self.isDirty = False - self.buttonBox.button(QtGui.QDialogButtonBox.Apply).setEnabled(False) - - def clicked(self, button): - # callback for standard buttons - if button == QtGui.QDialogButtonBox.Apply: - self.updateModel() - FreeCAD.ActiveDocument.recompute() - if button == QtGui.QDialogButtonBox.Cancel: - self.abort() - - def abort(self): - FreeCAD.ActiveDocument.abortTransaction() - self.cleanup(True) - - def reject(self): - FreeCAD.ActiveDocument.abortTransaction() - FreeCADGui.Control.closeDialog() - FreeCAD.ActiveDocument.recompute() - - def accept(self): - self.getFields() - FreeCAD.ActiveDocument.commitTransaction() - FreeCADGui.ActiveDocument.resetEdit() - FreeCADGui.Control.closeDialog() - FreeCAD.ActiveDocument.recompute() - - def cleanup(self, gui): - self.viewProvider.clearTaskPanel() - if gui: - #FreeCADGui.ActiveDocument.resetEdit() - FreeCADGui.Control.closeDialog() - FreeCAD.ActiveDocument.recompute() - - def getFields(self): - self.obj.LeadIn = self.form.chkLeadIn.isChecked() - self.obj.LeadOut = self.form.chkLeadOut.isChecked() - self.obj.Length = self.form.dsbLen.value() - self.obj.ExtendLeadIn = self.form.dsbExtendIn.value() - self.obj.ExtendLeadOut = self.form.dsbExtendOut.value() - self.obj.StyleOn = str(self.form.cboStyleIn.currentText()) - self.obj.StyleOff = str(self.form.cboStyleOut.currentText()) - self.obj.RadiusCenter = str(self.form.cboRadius.currentText()) - self.obj.RapidPlunge = self.form.chkRapidPlunge.isChecked() - self.obj.IncludeLayers = self.form.chkLayers.isChecked() - self.obj.KeepToolDown = self.form.chkKeepToolDown.isChecked() - self.obj.UseMachineCRC = self.form.chkUseCRC.isChecked() - - self.updateUI() - self.obj.Proxy.execute(self.obj) - - def updateUI(self): - self.form.chkLeadIn.setChecked(self.obj.LeadIn) - self.form.chkLeadOut.setChecked(self.obj.LeadOut) - self.form.chkRapidPlunge.setChecked(self.obj.RapidPlunge) - self.form.chkLayers.setChecked(self.obj.IncludeLayers) - self.form.chkKeepToolDown.setChecked(self.obj.KeepToolDown) - self.form.chkUseCRC.setChecked(self.obj.UseMachineCRC) - - self.form.dsbLen.setValue(self.obj.Length) - - self.form.dsbExtendIn.setValue(self.obj.ExtendLeadIn) - #self.form.dsbExtendIn.setEnabled(self.obj.LeadIn) - - self.form.dsbExtendOut.setValue(self.obj.ExtendLeadOut) - #self.form.dsbExtendOut.setEnabled(self.obj.LeadOut) - - self.form.cboStyleIn.setCurrentIndex(self.form.cboStyleIn.findText(self.obj.StyleOn)) - #self.form.cboStyleIn.setEnabled(self.obj.LeadIn) - - self.form.cboStyleOut.setCurrentIndex(self.form.cboStyleIn.findText(self.obj.StyleOff)) - #self.form.cboStyleOut.setEnabled(self.obj.LeadOut) - - self.form.cboRadius.setCurrentIndex(self.form.cboRadius.findText(self.obj.RadiusCenter)) - - def updateModel(self): - self.getFields() - FreeCAD.ActiveDocument.recompute() - - def setFields(self): - self.updateUI() - - def open(self): - pass +class TaskDressupLeadInOut(SimpleEditPanel): + _transaction_name = "Edit LeadInOut Dress-up" + _ui_file = ":/panels/DressUpLeadInOutEdit.ui" def setupUi(self): + self.connectWidget("LeadIn", self.form.chkLeadIn) + self.connectWidget("LeadOut", self.form.chkLeadOut) + self.connectWidget("Length", self.form.dsbLen) + self.connectWidget("ExtendLeadIn", self.form.dsbExtendIn) + self.connectWidget("ExtendLeadOut", self.form.dsbExtendOut) + self.connectWidget("StyleOn", self.form.cboStyleIn) + self.connectWidget("StyleOff", self.form.cboStyleOut) + self.connectWidget("RadiusCenter", self.form.cboRadius) + self.connectWidget("RapidPlunge", self.form.chkRapidPlunge) + self.connectWidget("IncludeLayers", self.form.chkLayers) + self.connectWidget("KeepToolDown", self.form.chkKeepToolDown) + self.connectWidget("UseMachineCRC", self.form.chkUseCRC) self.setFields() @@ -603,17 +636,16 @@ class ViewProviderDressup: def setEdit(self, vobj, mode=0): # pylint: disable=unused-argument FreeCADGui.Control.closeDialog() - panel = TaskPanel(vobj.Object, self) + panel = TaskDressupLeadInOut(vobj.Object, self) FreeCADGui.Control.showDialog(panel) - panel.setupUi() return True - + def unsetEdit(self, vobj, mode=0): if self.panel: self.panel.abort() def onDelete(self, arg1=None, arg2=None): - '''this makes sure that the base operation is added back to the project and visible''' + """this makes sure that the base operation is added back to the project and visible""" # pylint: disable=unused-argument PathLog.debug("Deleting Dressup") if arg1.Object and arg1.Object.Base: @@ -630,7 +662,7 @@ class ViewProviderDressup: def __setstate__(self, state): # pylint: disable=unused-argument return None - + def clearTaskPanel(self): self.panel = None @@ -639,9 +671,14 @@ class CommandPathDressupLeadInOut: # pylint: disable=no-init def GetResources(self): - return {'Pixmap': 'Path_Dressup', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_DressupLeadInOut", "LeadInOut Dressup"), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_DressupLeadInOut", "Creates a Cutter Radius Compensation G41/G42 Entry Dressup object from a selected path")} + return { + "Pixmap": "Path_Dressup", + "MenuText": QT_TRANSLATE_NOOP("Path_DressupLeadInOut", "LeadInOut Dressup"), + "ToolTip": QT_TRANSLATE_NOOP( + "Path_DressupLeadInOut", + "Creates a Cutter Radius Compensation G41/G42 Entry Dressup object from a selected path", + ), + } def IsActive(self): op = PathDressup.selection() @@ -653,35 +690,51 @@ class CommandPathDressupLeadInOut: # check that the selection contains exactly what we want selection = FreeCADGui.Selection.getSelection() if len(selection) != 1: - PathLog.error(translate("Please select one path object")+"\n") + PathLog.error( + translate("Path_DressupLeadInOut", "Please select one path object") + + "\n" + ) return baseObject = selection[0] if not baseObject.isDerivedFrom("Path::Feature"): - PathLog.error(translate("The selected object is not a path")+"\n") + PathLog.error( + translate("Path_DressupLeadInOut", "The selected object is not a path") + + "\n" + ) return if baseObject.isDerivedFrom("Path::FeatureCompoundPython"): - PathLog.error(translate("Please select a Profile object")) + PathLog.error( + translate("Path_DressupLeadInOut", "Please select a Profile object") + ) return # everything ok! - FreeCAD.ActiveDocument.openTransaction(translate("Create LeadInOut Dressup")) + FreeCAD.ActiveDocument.openTransaction("Create LeadInOut Dressup") FreeCADGui.addModule("PathScripts.PathDressupLeadInOut") FreeCADGui.addModule("PathScripts.PathUtils") - FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "LeadInOutDressup")') - FreeCADGui.doCommand('dbo = PathScripts.PathDressupLeadInOut.ObjectDressup(obj)') - FreeCADGui.doCommand('base = FreeCAD.ActiveDocument.' + selection[0].Name) - FreeCADGui.doCommand('job = PathScripts.PathUtils.findParentJob(base)') - FreeCADGui.doCommand('obj.Base = base') - FreeCADGui.doCommand('job.Proxy.addOperation(obj, base)') - FreeCADGui.doCommand('dbo.setup(obj)') - FreeCADGui.doCommand('obj.ViewObject.Proxy = PathScripts.PathDressupLeadInOut.ViewProviderDressup(obj.ViewObject)') - FreeCADGui.doCommand('Gui.ActiveDocument.getObject(base.Name).Visibility = False') + FreeCADGui.doCommand( + 'obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "LeadInOutDressup")' + ) + FreeCADGui.doCommand( + "dbo = PathScripts.PathDressupLeadInOut.ObjectDressup(obj)" + ) + FreeCADGui.doCommand("base = FreeCAD.ActiveDocument." + selection[0].Name) + FreeCADGui.doCommand("job = PathScripts.PathUtils.findParentJob(base)") + FreeCADGui.doCommand("obj.Base = base") + FreeCADGui.doCommand("job.Proxy.addOperation(obj, base)") + FreeCADGui.doCommand("dbo.setup(obj)") + FreeCADGui.doCommand( + "obj.ViewObject.Proxy = PathScripts.PathDressupLeadInOut.ViewProviderDressup(obj.ViewObject)" + ) + FreeCADGui.doCommand( + "Gui.ActiveDocument.getObject(base.Name).Visibility = False" + ) FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() if FreeCAD.GuiUp: # register the FreeCAD command - FreeCADGui.addCommand('Path_DressupLeadInOut', CommandPathDressupLeadInOut()) + FreeCADGui.addCommand("Path_DressupLeadInOut", CommandPathDressupLeadInOut()) -PathLog.notice("Loading PathDressupLeadInOut... done\n") +PathLog.notice("Loading Path_DressupLeadInOut... done\n")