diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui index 15708312cc..df014a5d7b 100644 --- a/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui @@ -62,26 +62,6 @@ <html><head/><body><p>Specify if the facing should be restricted by the actual shape of the selected face (or the part if no face is selected), or if the bounding box should be faced off.</p><p>The latter can be used to face of the entire stock area to ensure uniform heights for the following operations.</p></body></html> - - - Boundbox - - - - - Face Region - - - - - Perimeter - - - - - Stock - - diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py index 32f44f5dfb..feb07b5f78 100644 --- a/src/Mod/Path/InitGui.py +++ b/src/Mod/Path/InitGui.py @@ -112,7 +112,7 @@ class PathWorkbench(Workbench): "Path_Helix", "Path_Adaptive", ] - threedopcmdlist = ["Path_Pocket_3D"] + threedopcmdlist = ["Path_Pocket3D"] engravecmdlist = ["Path_Engrave", "Path_Deburr", "Path_Vcarve"] modcmdlist = ["Path_OperationCopy", "Path_Array", "Path_SimpleCopy"] dressupcmdlist = [ @@ -141,7 +141,7 @@ class PathWorkbench(Workbench): FreeCADGui.addCommand( "Path_EngraveTools", PathCommandGroup( - engravecmdlist, QT_TRANSLATE_NOOP("Path", "Engraving Operations") + engravecmdlist, QT_TRANSLATE_NOOP("Path_EngraveTools", "Engraving Operations") ), ) @@ -165,7 +165,7 @@ class PathWorkbench(Workbench): "Path_3dTools", PathCommandGroup( threedopcmdlist, - QT_TRANSLATE_NOOP("Path", "3D Operations"), + QT_TRANSLATE_NOOP("Path_3dTools", "3D Operations"), ), ) except ImportError: diff --git a/src/Mod/Path/PathScripts/PathEngrave.py b/src/Mod/Path/PathScripts/PathEngrave.py index a87287119c..6496658aaf 100644 --- a/src/Mod/Path/PathScripts/PathEngrave.py +++ b/src/Mod/Path/PathScripts/PathEngrave.py @@ -27,23 +27,21 @@ import PathScripts.PathLog as PathLog import PathScripts.PathOp as PathOp import PathScripts.PathUtils as PathUtils -from PySide import QtCore +from PySide.QtCore import QT_TRANSLATE_NOOP + +__doc__ = "Class and implementation of Path Engrave operation" + +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader Part = LazyLoader("Part", globals(), "Part") -__doc__ = "Class and implementation of Path Engrave operation" - -PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -# PathLog.trackModule(PathLog.thisModule()) - - -# Qt translation handling -def translate(context, text, disambig=None): - return QtCore.QCoreApplication.translate(context, text, disambig) - class ObjectEngrave(PathEngraveBase.ObjectOp): """Proxy class for Engrave operation.""" @@ -69,8 +67,8 @@ class ObjectEngrave(PathEngraveBase.ObjectOp): "App::PropertyLinkList", "BaseShapes", "Path", - QtCore.QT_TRANSLATE_NOOP( - "PathEngrave", "Additional base objects to be engraved" + QT_TRANSLATE_NOOP( + "App::Property", "Additional base objects to be engraved" ), ) obj.setEditorMode("BaseShapes", 2) # hide @@ -79,8 +77,8 @@ class ObjectEngrave(PathEngraveBase.ObjectOp): "App::PropertyLink", "BaseObject", "Path", - QtCore.QT_TRANSLATE_NOOP( - "PathEngrave", "Additional base objects to be engraved" + QT_TRANSLATE_NOOP( + "App::Property", "Additional base objects to be engraved" ), ) obj.setEditorMode("BaseObject", 2) # hide @@ -91,8 +89,8 @@ class ObjectEngrave(PathEngraveBase.ObjectOp): "App::PropertyInteger", "StartVertex", "Path", - QtCore.QT_TRANSLATE_NOOP( - "PathEngrave", "The vertex index to start the path from" + QT_TRANSLATE_NOOP( + "App::Property", "The vertex index to start the path from" ), ) self.setupAdditionalProperties(obj) diff --git a/src/Mod/Path/PathScripts/PathEngraveBase.py b/src/Mod/Path/PathScripts/PathEngraveBase.py index abaf413dc8..fab2c1dfaf 100644 --- a/src/Mod/Path/PathScripts/PathEngraveBase.py +++ b/src/Mod/Path/PathScripts/PathEngraveBase.py @@ -20,6 +20,7 @@ # * * # *************************************************************************** +from lazy_loader.lazy_loader import LazyLoader import Path import PathScripts.PathGeom as PathGeom import PathScripts.PathLog as PathLog @@ -27,24 +28,21 @@ import PathScripts.PathOp as PathOp import PathScripts.PathOpTools as PathOpTools import copy -# lazily loaded modules -from lazy_loader.lazy_loader import LazyLoader -DraftGeomUtils = LazyLoader('DraftGeomUtils', globals(), 'DraftGeomUtils') -Part = LazyLoader('Part', globals(), 'Part') - -from PySide import QtCore - __doc__ = "Base class for all ops in the engrave family." -PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) +# lazily loaded modules +DraftGeomUtils = LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils") +Part = LazyLoader("Part", globals(), "Part") + +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -# Qt translation handling -def translate(context, text, disambig=None): - return QtCore.QCoreApplication.translate(context, text, disambig) class ObjectOp(PathOp.ObjectOp): - '''Proxy base class for engrave operations.''' + """Proxy base class for engrave operations.""" def getZValues(self, obj): zValues = [] @@ -62,47 +60,79 @@ class ObjectOp(PathOp.ObjectOp): return zValues def buildpathocc(self, obj, wires, zValues, relZ=False, forward=True, start_idx=0): - '''buildpathocc(obj, wires, zValues, relZ=False) ... internal helper function to generate engraving commands.''' + """buildpathocc(obj, wires, zValues, relZ=False) ... internal helper function to generate engraving commands.""" PathLog.track(obj.Label, len(wires), zValues) for wire in wires: offset = wire # reorder the wire - if hasattr(obj, 'StartVertex'): + if hasattr(obj, "StartVertex"): start_idx = obj.StartVertex edges = copy.copy(PathOpTools.orientWire(offset, forward).Edges) - edges = Part.sortEdges(edges)[0]; + edges = Part.sortEdges(edges)[0] last = None for z in zValues: PathLog.debug(z) if last: - self.appendCommand(Path.Command('G1', {'X': last.x, 'Y': last.y, 'Z': last.z}), z, relZ, self.vertFeed) + self.appendCommand( + Path.Command("G1", {"X": last.x, "Y": last.y, "Z": last.z}), + z, + relZ, + self.vertFeed, + ) first = True - if start_idx > len(edges)-1: - start_idx = len(edges)-1 + if start_idx > len(edges) - 1: + start_idx = len(edges) - 1 edges = edges[start_idx:] + edges[:start_idx] for edge in edges: - PathLog.debug("points: {} -> {}".format(edge.Vertexes[0].Point, edge.Vertexes[-1].Point)) - PathLog.debug("valueat {} -> {}".format(edge.valueAt(edge.FirstParameter), edge.valueAt(edge.LastParameter))) + PathLog.debug( + "points: {} -> {}".format( + edge.Vertexes[0].Point, edge.Vertexes[-1].Point + ) + ) + PathLog.debug( + "valueat {} -> {}".format( + edge.valueAt(edge.FirstParameter), + edge.valueAt(edge.LastParameter), + ) + ) if first and (not last or not wire.isClosed()): - PathLog.debug('processing first edge entry') + PathLog.debug("processing first edge entry") # we set the first move to our first point last = edge.Vertexes[0].Point - self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) - self.commandlist.append(Path.Command('G0', {'X': last.x, 'Y': last.y, 'F': self.horizRapid})) - self.commandlist.append(Path.Command('G0', {'Z': obj.SafeHeight.Value, 'F': self.vertRapid})) - self.appendCommand(Path.Command('G1', {'X': last.x, 'Y': last.y, 'Z': last.z}), z, relZ, self.vertFeed) + self.commandlist.append( + Path.Command( + "G0", + {"Z": obj.ClearanceHeight.Value, "F": self.vertRapid}, + ) + ) + self.commandlist.append( + Path.Command( + "G0", {"X": last.x, "Y": last.y, "F": self.horizRapid} + ) + ) + self.commandlist.append( + Path.Command( + "G0", {"Z": obj.SafeHeight.Value, "F": self.vertRapid} + ) + ) + self.appendCommand( + Path.Command("G1", {"X": last.x, "Y": last.y, "Z": last.z}), + z, + relZ, + self.vertFeed, + ) first = False if PathGeom.pointsCoincide(last, edge.valueAt(edge.FirstParameter)): - #if PathGeom.pointsCoincide(last, edge.Vertexes[0].Point): + # if PathGeom.pointsCoincide(last, edge.Vertexes[0].Point): for cmd in PathGeom.cmdsForEdge(edge): self.appendCommand(cmd, z, relZ, self.horizFeed) last = edge.Vertexes[-1].Point @@ -110,17 +140,21 @@ class ObjectOp(PathOp.ObjectOp): for cmd in PathGeom.cmdsForEdge(edge, True): self.appendCommand(cmd, z, relZ, self.horizFeed) last = edge.Vertexes[0].Point - self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid})) + self.commandlist.append( + Path.Command( + "G0", {"Z": obj.ClearanceHeight.Value, "F": self.vertRapid} + ) + ) def appendCommand(self, cmd, z, relZ, feed): params = cmd.Parameters if relZ: - z = params['Z'] - z - params.update({'Z': z, 'F': feed}) + z = params["Z"] - z + params.update({"Z": z, "F": feed}) self.commandlist.append(Path.Command(cmd.Name, params)) def opSetDefaultValues(self, obj, job): - '''opSetDefaultValues(obj) ... set depths for engraving''' + """opSetDefaultValues(obj) ... set depths for engraving""" if PathOp.FeatureDepths & self.opFeatures(obj): if job and len(job.Model.Group) > 0: bb = job.Proxy.modelBoundBox(job) @@ -128,4 +162,3 @@ class ObjectOp(PathOp.ObjectOp): obj.OpFinalDepth = bb.ZMax - max(obj.StepDown.Value, 0.1) else: obj.OpFinalDepth = -0.1 - diff --git a/src/Mod/Path/PathScripts/PathEngraveGui.py b/src/Mod/Path/PathScripts/PathEngraveGui.py index 6301c4882d..d90064e009 100644 --- a/src/Mod/Path/PathScripts/PathEngraveGui.py +++ b/src/Mod/Path/PathScripts/PathEngraveGui.py @@ -22,7 +22,7 @@ import FreeCAD import FreeCADGui -import PathGui as PGui # ensure Path/Gui/Resources are loaded +import PathGui as PGui # ensure Path/Gui/Resources are loaded import PathScripts.PathEngrave as PathEngrave import PathScripts.PathLog as PathLog import PathScripts.PathOpGui as PathOpGui @@ -30,26 +30,35 @@ import PathScripts.PathUtils as PathUtils from PySide import QtCore, QtGui + __title__ = "Path Engrave Operation UI" __author__ = "sliptonic (Brad Collette)" __url__ = "https://www.freecadweb.org" __doc__ = "Engrave operation page controller and command implementation." -PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) + + +translate = FreeCAD.Qt.translate -def translate(context, text, disambig=None): - return QtCore.QCoreApplication.translate(context, text, disambig) class TaskPanelBaseGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): - '''Enhanced base geometry page to also allow special base objects.''' + """Enhanced base geometry page to also allow special base objects.""" def super(self): return super(TaskPanelBaseGeometryPage, self) def selectionSupportedAsBaseGeometry(self, selection, ignoreErrors): # allow selection of an entire 2D object, which is generally not the case - if len(selection) == 1 and not selection[0].HasSubObjects and selection[0].Object.isDerivedFrom('Part::Part2DObject'): + if ( + len(selection) == 1 + and not selection[0].HasSubObjects + and selection[0].Object.isDerivedFrom("Part::Part2DObject") + ): return True # Let general logic handle all other cases. return self.super().selectionSupportedAsBaseGeometry(selection, ignoreErrors) @@ -61,22 +70,31 @@ class TaskPanelBaseGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): job = PathUtils.findParentJob(self.obj) base = job.Proxy.resourceClone(job, sel.Object) if not base: - PathLog.notice((translate("Path", "%s is not a Base Model object of the job %s")+"\n") % (sel.Object.Label, job.Label)) + PathLog.notice( + ( + translate("Path", "%s is not a Base Model object of the job %s") + + "\n" + ) + % (sel.Object.Label, job.Label) + ) continue if base in shapes: - PathLog.notice((translate("Path", "Base shape %s already in the list")+"\n") % (sel.Object.Label)) + PathLog.notice( + (translate("Path", "Base shape %s already in the list") + "\n") + % (sel.Object.Label) + ) continue - if base.isDerivedFrom('Part::Part2DObject'): + if base.isDerivedFrom("Part::Part2DObject"): if sel.HasSubObjects: # selectively add some elements of the drawing to the Base for sub in sel.SubElementNames: - if 'Vertex' in sub: - PathLog.info(translate("Path", "Ignoring vertex")) + if "Vertex" in sub: + PathLog.info("Ignoring vertex") else: self.obj.Proxy.addBase(self.obj, base, sub) else: # when adding an entire shape to BaseShapes we can take its sub shapes out of Base - self.obj.Base = [(p,el) for p,el in self.obj.Base if p != base] + self.obj.Base = [(p, el) for p, el in self.obj.Base if p != base] shapes.append(base) self.obj.BaseShapes = shapes added = True @@ -106,32 +124,35 @@ class TaskPanelBaseGeometryPage(PathOpGui.TaskPanelBaseGeometryPage): sub = item.data(self.super().DataObjectSub) if not sub: shapes.append(obj) - PathLog.debug("Setting new base shapes: %s -> %s" % (self.obj.BaseShapes, shapes)) + PathLog.debug( + "Setting new base shapes: %s -> %s" % (self.obj.BaseShapes, shapes) + ) self.obj.BaseShapes = shapes return self.super().updateBase() + class TaskPanelOpPage(PathOpGui.TaskPanelPage): - '''Page controller class for the Engrave operation.''' + """Page controller class for the Engrave operation.""" def getForm(self): - '''getForm() ... returns UI''' + """getForm() ... returns UI""" return FreeCADGui.PySideUic.loadUi(":/panels/PageOpEngraveEdit.ui") def getFields(self, obj): - '''getFields(obj) ... transfers values from UI to obj's proprties''' + """getFields(obj) ... transfers values from UI to obj's proprties""" if obj.StartVertex != self.form.startVertex.value(): obj.StartVertex = self.form.startVertex.value() self.updateToolController(obj, self.form.toolController) self.updateCoolant(obj, self.form.coolantController) def setFields(self, obj): - '''setFields(obj) ... transfers obj's property values to UI''' + """setFields(obj) ... transfers obj's property values to UI""" self.form.startVertex.setValue(obj.StartVertex) self.setupToolController(obj, self.form.toolController) self.setupCoolant(obj, self.form.coolantController) def getSignalsForUpdate(self, obj): - '''getSignalsForUpdate(obj) ... return list of signals for updating obj''' + """getSignalsForUpdate(obj) ... return list of signals for updating obj""" signals = [] signals.append(self.form.startVertex.editingFinished) signals.append(self.form.toolController.currentIndexChanged) @@ -139,15 +160,20 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): return signals def taskPanelBaseGeometryPage(self, obj, features): - '''taskPanelBaseGeometryPage(obj, features) ... return page for adding base geometries.''' + """taskPanelBaseGeometryPage(obj, features) ... return page for adding base geometries.""" return TaskPanelBaseGeometryPage(obj, features) -Command = PathOpGui.SetupOperation('Engrave', - PathEngrave.Create, - TaskPanelOpPage, - 'Path_Engrave', - QtCore.QT_TRANSLATE_NOOP("PathEngrave", "Engrave"), - QtCore.QT_TRANSLATE_NOOP("PathEngrave", "Creates an Engraving Path around a Draft ShapeString"), - PathEngrave.SetupProperties) + +Command = PathOpGui.SetupOperation( + "Engrave", + PathEngrave.Create, + TaskPanelOpPage, + "Path_Engrave", + QtCore.QT_TRANSLATE_NOOP("Path_Engrave", "Engrave"), + QtCore.QT_TRANSLATE_NOOP( + "Path_Engrave", "Creates an Engraving Path around a Draft ShapeString" + ), + PathEngrave.SetupProperties, +) FreeCAD.Console.PrintLog("Loading PathEngraveGui... done\n") diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py index ec9a528880..a37b928912 100644 --- a/src/Mod/Path/PathScripts/PathMillFace.py +++ b/src/Mod/Path/PathScripts/PathMillFace.py @@ -26,13 +26,13 @@ import FreeCAD import PathScripts.PathLog as PathLog import PathScripts.PathPocketBase as PathPocketBase import PathScripts.PathUtils as PathUtils - -from PySide import QtCore +from PySide.QtCore import QT_TRANSLATE_NOOP import numpy # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader -Part = LazyLoader('Part', globals(), 'Part') + +Part = LazyLoader("Part", globals(), "Part") __title__ = "Path Mill Face Operation" __author__ = "sliptonic (Brad Collette)" @@ -41,32 +41,87 @@ __doc__ = "Class and implementation of Mill Facing operation." __contributors__ = "russ4262 (Russell Johnson)" -PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -# PathLog.trackModule() +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) - -# Qt translation handling -def translate(context, text, disambig=None): - return QtCore.QCoreApplication.translate(context, text, disambig) +translate = FreeCAD.Qt.translate class ObjectFace(PathPocketBase.ObjectPocket): - '''Proxy object for Mill Facing operation.''' + """Proxy object for Mill Facing operation.""" + + @classmethod + def propertyEnumerations(self, dataType="data"): + """helixOpPropertyEnumerations(dataType="data")... return property enumeration lists of specified dataType. + Args: + dataType = 'data', 'raw', 'translated' + Notes: + 'data' is list of internal string literals used in code + 'raw' is list of (translated_text, data_string) tuples + 'translated' is list of translated string literals + """ + + enums = { + "BoundaryShape": [ + (translate("Path_Pocket", "Boundbox"), "Boundbox"), + (translate("Path_Pocket", "Face Region"), "Face Region"), + (translate("Path_Pocket", "Perimeter"), "Perimeter"), + (translate("Path_Pocket", "Stock"), "Stock"), + ], + } + + if dataType == "raw": + return enums + + data = list() + idx = 0 if dataType == "translated" else 1 + + PathLog.debug(enums) + + for k, v in enumerate(enums): + data.append((v, [tup[idx] for tup in enums[v]])) + PathLog.debug(data) + + return data def initPocketOp(self, obj): - '''initPocketOp(obj) ... create facing specific properties''' - obj.addProperty("App::PropertyEnumeration", "BoundaryShape", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Shape to use for calculating Boundary")) - obj.addProperty("App::PropertyBool", "ClearEdges", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Clear edges of surface (Only applicable to BoundBox)")) - if not hasattr(obj, 'ExcludeRaisedAreas'): - obj.addProperty("App::PropertyBool", "ExcludeRaisedAreas", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Exclude milling raised areas inside the face.")) + PathLog.track() + """initPocketOp(obj) ... create facing specific properties""" + obj.addProperty( + "App::PropertyEnumeration", + "BoundaryShape", + "Face", + QT_TRANSLATE_NOOP("App::Property", "Shape to use for calculating Boundary"), + ) + obj.addProperty( + "App::PropertyBool", + "ClearEdges", + "Face", + QT_TRANSLATE_NOOP( + "App::Property", "Clear edges of surface (Only applicable to BoundBox)" + ), + ) + if not hasattr(obj, "ExcludeRaisedAreas"): + obj.addProperty( + "App::PropertyBool", + "ExcludeRaisedAreas", + "Face", + QT_TRANSLATE_NOOP( + "App::Property", "Exclude milling raised areas inside the face." + ), + ) - obj.BoundaryShape = ['Boundbox', 'Face Region', 'Perimeter', 'Stock'] + for n in self.propertyEnumerations(): + setattr(obj, n[0], n[1]) def pocketInvertExtraOffset(self): return True def areaOpOnChanged(self, obj, prop): - '''areaOpOnChanged(obj, prop) ... facing specific depths calculation.''' + """areaOpOnChanged(obj, prop) ... facing specific depths calculation.""" PathLog.track(prop) if prop == "StepOver" and obj.StepOver == 0: obj.StepOver = 1 @@ -78,27 +133,27 @@ class ObjectFace(PathPocketBase.ObjectPocket): obj.OpStartDepth = job.Stock.Shape.BoundBox.ZMax if len(obj.Base) >= 1: - PathLog.debug('processing') + PathLog.debug("processing") sublist = [] for i in obj.Base: o = i[0] for s in i[1]: sublist.append(o.Shape.getElement(s)) - # If the operation has a geometry identified the Finaldepth - # is the top of the bboundbox which includes all features. - # Otherwise, top of part. + # If the operation has a geometry identified the Finaldepth + # is the top of the bboundbox which includes all features. + # Otherwise, top of part. obj.OpFinalDepth = Part.makeCompound(sublist).BoundBox.ZMax elif job: obj.OpFinalDepth = job.Proxy.modelBoundBox(job).ZMax def areaOpShapes(self, obj): - '''areaOpShapes(obj) ... return top face''' + """areaOpShapes(obj) ... return top face""" # Facing is done either against base objects holeShape = None - PathLog.debug('depthparams: {}'.format([i for i in self.depthparams])) + PathLog.debug("depthparams: {}".format([i for i in self.depthparams])) if obj.Base: PathLog.debug("obj.Base: {}".format(obj.Base)) @@ -120,7 +175,9 @@ class ObjectFace(PathPocketBase.ObjectPocket): # Limit to one model base per operation if oneBase[0] is not b[0]: oneBase[1] = False - if numpy.isclose(abs(shape.normalAt(0, 0).z), 1): # horizontal face + if numpy.isclose( + abs(shape.normalAt(0, 0).z), 1 + ): # horizontal face # Analyze internal closed wires to determine if raised or a recess for wire in shape.Wires[1:]: if obj.ExcludeRaisedAreas: @@ -130,12 +187,18 @@ class ObjectFace(PathPocketBase.ObjectPocket): else: holes.append((b[0].Shape, wire)) else: - PathLog.warning('The base subobject, "{0}," is not a face. Ignoring "{0}."'.format(sub)) + PathLog.warning( + 'The base subobject, "{0}," is not a face. Ignoring "{0}."'.format( + sub + ) + ) if obj.ExcludeRaisedAreas and len(holes) > 0: for shape, wire in holes: - f = Part.makeFace(wire, 'Part::FaceMakerSimple') - env = PathUtils.getEnvelope(shape, subshape=f, depthparams=self.depthparams) + f = Part.makeFace(wire, "Part::FaceMakerSimple") + env = PathUtils.getEnvelope( + shape, subshape=f, depthparams=self.depthparams + ) holeEnvs.append(env) holeShape = Part.makeCompound(holeEnvs) @@ -161,62 +224,98 @@ class ObjectFace(PathPocketBase.ObjectPocket): bb.XMax = bb.XMax + offset bb.YMax = bb.YMax + offset - if obj.BoundaryShape == 'Boundbox': - bbperim = Part.makeBox(bb.XLength, bb.YLength, 1, FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), FreeCAD.Vector(0, 0, 1)) + if obj.BoundaryShape == "Boundbox": + bbperim = Part.makeBox( + bb.XLength, + bb.YLength, + 1, + FreeCAD.Vector(bb.XMin, bb.YMin, bb.ZMin), + FreeCAD.Vector(0, 0, 1), + ) env = PathUtils.getEnvelope(partshape=bbperim, depthparams=self.depthparams) if obj.ExcludeRaisedAreas and oneBase[1]: - includedFaces = self.getAllIncludedFaces(oneBase[0], env, faceZ=minHeight) + includedFaces = self.getAllIncludedFaces( + oneBase[0], env, faceZ=minHeight + ) if len(includedFaces) > 0: includedShape = Part.makeCompound(includedFaces) - includedEnv = PathUtils.getEnvelope(oneBase[0].Shape, subshape=includedShape, depthparams=self.depthparams) + includedEnv = PathUtils.getEnvelope( + oneBase[0].Shape, + subshape=includedShape, + depthparams=self.depthparams, + ) env = env.cut(includedEnv) - elif obj.BoundaryShape == 'Stock': + elif obj.BoundaryShape == "Stock": stock = PathUtils.findParentJob(obj).Stock.Shape env = stock if obj.ExcludeRaisedAreas and oneBase[1]: - includedFaces = self.getAllIncludedFaces(oneBase[0], stock, faceZ=minHeight) + includedFaces = self.getAllIncludedFaces( + oneBase[0], stock, faceZ=minHeight + ) if len(includedFaces) > 0: - stockEnv = PathUtils.getEnvelope(partshape=stock, depthparams=self.depthparams) + stockEnv = PathUtils.getEnvelope( + partshape=stock, depthparams=self.depthparams + ) includedShape = Part.makeCompound(includedFaces) - includedEnv = PathUtils.getEnvelope(oneBase[0].Shape, subshape=includedShape, depthparams=self.depthparams) + includedEnv = PathUtils.getEnvelope( + oneBase[0].Shape, + subshape=includedShape, + depthparams=self.depthparams, + ) env = stockEnv.cut(includedEnv) - elif obj.BoundaryShape == 'Perimeter': + elif obj.BoundaryShape == "Perimeter": if obj.ClearEdges: psZMin = planeshape.BoundBox.ZMin - ofstShape = PathUtils.getOffsetArea(planeshape, - self.radius * 1.25, - plane=planeshape) - ofstShape.translate(FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin)) - env = PathUtils.getEnvelope(partshape=ofstShape, depthparams=self.depthparams) + ofstShape = PathUtils.getOffsetArea( + planeshape, self.radius * 1.25, plane=planeshape + ) + ofstShape.translate( + FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin) + ) + env = PathUtils.getEnvelope( + partshape=ofstShape, depthparams=self.depthparams + ) else: - env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams) - elif obj.BoundaryShape == 'Face Region': + env = PathUtils.getEnvelope( + partshape=planeshape, depthparams=self.depthparams + ) + elif obj.BoundaryShape == "Face Region": baseShape = oneBase[0].Shape psZMin = planeshape.BoundBox.ZMin ofst = 0.0 if obj.ClearEdges: ofst = self.tool.Diameter * 0.51 ofstShape = PathUtils.getOffsetArea(planeshape, ofst, plane=planeshape) - ofstShape.translate(FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin)) + ofstShape.translate( + FreeCAD.Vector(0.0, 0.0, psZMin - ofstShape.BoundBox.ZMin) + ) # Calculate custom depth params for removal shape envelope, with start and final depth buffers - custDepthparams = self._customDepthParams(obj, obj.StartDepth.Value + 0.2, obj.FinalDepth.Value - 0.1) # only an envelope - ofstShapeEnv = PathUtils.getEnvelope(partshape=ofstShape, depthparams=custDepthparams) - if obj.ExcludeRaisedAreas: + custDepthparams = self._customDepthParams( + obj, obj.StartDepth.Value + 0.2, obj.FinalDepth.Value - 0.1 + ) # only an envelope + ofstShapeEnv = PathUtils.getEnvelope( + partshape=ofstShape, depthparams=custDepthparams + ) + if obj.ExcludeRaisedAreas: env = ofstShapeEnv.cut(baseShape) - env.translate(FreeCAD.Vector(0.0, 0.0, -0.00001)) # lower removal shape into buffer zone + env.translate( + FreeCAD.Vector(0.0, 0.0, -0.00001) + ) # lower removal shape into buffer zone else: env = ofstShapeEnv if holeShape: PathLog.debug("Processing holes and face ...") - holeEnv = PathUtils.getEnvelope(partshape=holeShape, depthparams=self.depthparams) + holeEnv = PathUtils.getEnvelope( + partshape=holeShape, depthparams=self.depthparams + ) newEnv = env.cut(holeEnv) - tup = newEnv, False, 'pathMillFace' + tup = newEnv, False, "pathMillFace" else: PathLog.debug("Processing solid face ...") - tup = env, False, 'pathMillFace' + tup = env, False, "pathMillFace" self.removalshapes.append(tup) obj.removalshape = self.removalshapes[0][0] # save removal shape @@ -224,7 +323,7 @@ class ObjectFace(PathPocketBase.ObjectPocket): return self.removalshapes def areaOpSetDefaultValues(self, obj, job): - '''areaOpSetDefaultValues(obj, job) ... initialize mill facing properties''' + """areaOpSetDefaultValues(obj, job) ... initialize mill facing properties""" obj.StepOver = 50 obj.ZigZagAngle = 45.0 obj.ExcludeRaisedAreas = False @@ -260,8 +359,8 @@ class ObjectFace(PathPocketBase.ObjectPocket): return False def getAllIncludedFaces(self, base, env, faceZ): - '''getAllIncludedFaces(base, env, faceZ)... - Return all `base` faces extending above `faceZ` whose boundboxes overlap with the `env` boundbox.''' + """getAllIncludedFaces(base, env, faceZ)... + Return all `base` faces extending above `faceZ` whose boundboxes overlap with the `env` boundbox.""" included = [] eXMin = env.BoundBox.XMin @@ -305,7 +404,7 @@ def SetupProperties(): def Create(name, obj=None, parentJob=None): - '''Create(name) ... Creates and returns a Mill Facing operation.''' + """Create(name) ... Creates and returns a Mill Facing operation.""" if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) obj.Proxy = ObjectFace(obj, name, parentJob) diff --git a/src/Mod/Path/PathScripts/PathMillFaceGui.py b/src/Mod/Path/PathScripts/PathMillFaceGui.py index 104b7f9e09..5345629156 100644 --- a/src/Mod/Path/PathScripts/PathMillFaceGui.py +++ b/src/Mod/Path/PathScripts/PathMillFaceGui.py @@ -20,32 +20,79 @@ # * * # *************************************************************************** +from PySide.QtCore import QT_TRANSLATE_NOOP import FreeCAD +import PathScripts.PathLog as PathLog import PathScripts.PathMillFace as PathMillFace import PathScripts.PathOpGui as PathOpGui import PathScripts.PathPocketBaseGui as PathPocketBaseGui - -from PySide import QtCore +import PathScripts.PathPocketShape as PathPocketShape +import FreeCADGui __title__ = "Path Face Mill Operation UI" __author__ = "sliptonic (Brad Collette)" __url__ = "https://www.freecadweb.org" __doc__ = "Face Mill operation page controller and command implementation." +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) + + class TaskPanelOpPage(PathPocketBaseGui.TaskPanelOpPage): - '''Page controller class for the face milling operation.''' + """Page controller class for the face milling operation.""" + + def getForm(self): + PathLog.track() + """getForm() ... return UI""" + + form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpPocketFullEdit.ui") + comboToPropertyMap = [ + ("cutMode", "CutMode"), + ("offsetPattern", "OffsetPattern"), + ("boundaryShape", "BoundaryShape"), + ] + + enumTups = PathMillFace.ObjectFace.propertyEnumerations(dataType="raw") + enumTups.update( + PathPocketShape.ObjectPocket.pocketPropertyEnumerations(dataType="raw") + ) + + self.populateCombobox(form, enumTups, comboToPropertyMap) + return form + + def populateCombobox(self, form, enumTups, comboBoxesPropertyMap): + """fillComboboxes(form, comboBoxesPropertyMap) ... populate comboboxes with translated enumerations + ** comboBoxesPropertyMap will be unnecessary if UI files use strict combobox naming protocol. + Args: + form = UI form + enumTups = list of (translated_text, data_string) tuples + comboBoxesPropertyMap = list of (translated_text, data_string) tuples + """ + # Load appropriate enumerations in each combobox + for cb, prop in comboBoxesPropertyMap: + box = getattr(form, cb) # Get the combobox + box.clear() # clear the combobox + for text, data in enumTups[prop]: # load enumerations + box.addItem(text, data) def pocketFeatures(self): - '''pocketFeatures() ... return FeatureFacing (see PathPocketBaseGui)''' + """pocketFeatures() ... return FeatureFacing (see PathPocketBaseGui)""" return PathPocketBaseGui.FeatureFacing -Command = PathOpGui.SetupOperation('MillFace', - PathMillFace.Create, - TaskPanelOpPage, - 'Path_Face', - QtCore.QT_TRANSLATE_NOOP("Path_Face", "Face"), - QtCore.QT_TRANSLATE_NOOP("Path_Face", "Create a Facing Operation from a model or face"), - PathMillFace.SetupProperties) + +Command = PathOpGui.SetupOperation( + "MillFace", + PathMillFace.Create, + TaskPanelOpPage, + "Path_Face", + QT_TRANSLATE_NOOP("Path_MillFace", "Face"), + QT_TRANSLATE_NOOP( + "Path_MillFace", "Create a Facing Operation from a model or face" + ), + PathMillFace.SetupProperties, +) FreeCAD.Console.PrintLog("Loading PathMillFaceGui... done\n") - diff --git a/src/Mod/Path/PathScripts/PathPocket.py b/src/Mod/Path/PathScripts/PathPocket.py index 65249c6b20..8e5eb07986 100644 --- a/src/Mod/Path/PathScripts/PathPocket.py +++ b/src/Mod/Path/PathScripts/PathPocket.py @@ -20,17 +20,17 @@ # * * # *************************************************************************** +from PySide.QtCore import QT_TRANSLATE_NOOP import FreeCAD import PathScripts.PathLog as PathLog import PathScripts.PathOp as PathOp import PathScripts.PathPocketBase as PathPocketBase import PathScripts.PathUtils as PathUtils -from PySide import QtCore - # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader -Part = LazyLoader('Part', globals(), 'Part') + +Part = LazyLoader("Part", globals(), "Part") __title__ = "Path 3D Pocket Operation" __author__ = "Yorik van Havre " @@ -41,42 +41,110 @@ __created__ = "2014" __scriptVersion__ = "2e" __lastModified__ = "2020-02-13 17:22 CST" -PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -# PathLog.trackModule(PathLog.thisModule()) +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -# Qt translation handling -def translate(context, text, disambig=None): - return QtCore.QCoreApplication.translate(context, text, disambig) +translate = FreeCAD.Qt.translate class ObjectPocket(PathPocketBase.ObjectPocket): - '''Proxy object for Pocket operation.''' + """Proxy object for Pocket operation.""" def pocketOpFeatures(self, obj): return PathOp.FeatureNoFinalDepth def initPocketOp(self, obj): - '''initPocketOp(obj) ... setup receiver''' - if not hasattr(obj, 'HandleMultipleFeatures'): - obj.addProperty('App::PropertyEnumeration', 'HandleMultipleFeatures', 'Pocket', QtCore.QT_TRANSLATE_NOOP('PathPocket', 'Choose how to process multiple Base Geometry features.')) - obj.HandleMultipleFeatures = ['Collectively', 'Individually'] - if not hasattr(obj, 'AdaptivePocketStart'): - obj.addProperty('App::PropertyBool', 'AdaptivePocketStart', 'Pocket', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Use adaptive algorithm to eliminate excessive air milling above planar pocket top.')) - if not hasattr(obj, 'AdaptivePocketFinish'): - obj.addProperty('App::PropertyBool', 'AdaptivePocketFinish', 'Pocket', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Use adaptive algorithm to eliminate excessive air milling below planar pocket bottom.')) - if not hasattr(obj, 'ProcessStockArea'): - obj.addProperty('App::PropertyBool', 'ProcessStockArea', 'Pocket', QtCore.QT_TRANSLATE_NOOP('App::Property', 'Process the model and stock in an operation with no Base Geometry selected.')) + """initPocketOp(obj) ... setup receiver""" + if not hasattr(obj, "HandleMultipleFeatures"): + obj.addProperty( + "App::PropertyEnumeration", + "HandleMultipleFeatures", + "Pocket", + QT_TRANSLATE_NOOP( + "App::Property", + "Choose how to process multiple Base Geometry features.", + ), + ) + if not hasattr(obj, "AdaptivePocketStart"): + obj.addProperty( + "App::PropertyBool", + "AdaptivePocketStart", + "Pocket", + QT_TRANSLATE_NOOP( + "App::Property", + "Use adaptive algorithm to eliminate excessive air milling above planar pocket top.", + ), + ) + if not hasattr(obj, "AdaptivePocketFinish"): + obj.addProperty( + "App::PropertyBool", + "AdaptivePocketFinish", + "Pocket", + QT_TRANSLATE_NOOP( + "App::Property", + "Use adaptive algorithm to eliminate excessive air milling below planar pocket bottom.", + ), + ) + if not hasattr(obj, "ProcessStockArea"): + obj.addProperty( + "App::PropertyBool", + "ProcessStockArea", + "Pocket", + QT_TRANSLATE_NOOP( + "App::Property", + "Process the model and stock in an operation with no Base Geometry selected.", + ), + ) + + # populate the property enumerations + for n in self.propertyEnumerations(): + setattr(obj, n[0], n[1]) + + @classmethod + def propertyEnumerations(self, dataType="data"): + """propertyEnumerations(dataType="data")... return property enumeration lists of specified dataType. + Args: + dataType = 'data', 'raw', 'translated' + Notes: + 'data' is list of internal string literals used in code + 'raw' is list of (translated_text, data_string) tuples + 'translated' is list of translated string literals + """ + + enums = { + "HandleMultipleFeatures": [ + (translate("Path_Pocket", "Collectively"), "Collectively"), + (translate("Path_Pocket", "Individually"), "Individually"), + ], + } + + if dataType == "raw": + return enums + + data = list() + idx = 0 if dataType == "translated" else 1 + + PathLog.debug(enums) + + for k, v in enumerate(enums): + data.append((v, [tup[idx] for tup in enums[v]])) + PathLog.debug(data) + + return data def opOnDocumentRestored(self, obj): - '''opOnDocumentRestored(obj) ... adds the properties if they doesn't exist.''' + """opOnDocumentRestored(obj) ... adds the properties if they doesn't exist.""" self.initPocketOp(obj) def pocketInvertExtraOffset(self): return False def areaOpShapes(self, obj): - '''areaOpShapes(obj) ... return shapes representing the solids to be removed.''' + """areaOpShapes(obj) ... return shapes representing the solids to be removed.""" PathLog.track() subObjTups = [] @@ -102,34 +170,51 @@ class ObjectPocket(PathPocketBase.ObjectPocket): if len(Faces) == 0: allSubsFaceType = False - if allSubsFaceType is True and obj.HandleMultipleFeatures == 'Collectively': + if ( + allSubsFaceType is True + and obj.HandleMultipleFeatures == "Collectively" + ): (fzmin, fzmax) = self.getMinMaxOfFaces(Faces) if obj.FinalDepth.Value < fzmin: - PathLog.warning(translate('PathPocket', 'Final depth set below ZMin of face(s) selected.')) + PathLog.warning( + translate( + "PathPocket", + "Final depth set below ZMin of face(s) selected.", + ) + ) - if obj.AdaptivePocketStart is True or obj.AdaptivePocketFinish is True: + if ( + obj.AdaptivePocketStart is True + or obj.AdaptivePocketFinish is True + ): pocketTup = self.calculateAdaptivePocket(obj, base, subObjTups) if pocketTup is not False: obj.removalshape = pocketTup[0] removalshapes.append(pocketTup) # (shape, isHole, detail) else: shape = Part.makeCompound(Faces) - env = PathUtils.getEnvelope(base[0].Shape, subshape=shape, depthparams=self.depthparams) + env = PathUtils.getEnvelope( + base[0].Shape, subshape=shape, depthparams=self.depthparams + ) obj.removalshape = env.cut(base[0].Shape) # obj.removalshape.tessellate(0.1) - removalshapes.append((obj.removalshape, False, '3DPocket')) # (shape, isHole, detail) + removalshapes.append( + (obj.removalshape, False, "3DPocket") + ) # (shape, isHole, detail) else: for sub in base[1]: if "Face" in sub: shape = Part.makeCompound([getattr(base[0].Shape, sub)]) else: edges = [getattr(base[0].Shape, sub) for sub in base[1]] - shape = Part.makeFace(edges, 'Part::FaceMakerSimple') + shape = Part.makeFace(edges, "Part::FaceMakerSimple") - env = PathUtils.getEnvelope(base[0].Shape, subshape=shape, depthparams=self.depthparams) + env = PathUtils.getEnvelope( + base[0].Shape, subshape=shape, depthparams=self.depthparams + ) obj.removalshape = env.cut(base[0].Shape) # obj.removalshape.tessellate(0.1) - removalshapes.append((obj.removalshape, False, '3DPocket')) + removalshapes.append((obj.removalshape, False, "3DPocket")) else: # process the job base object as a whole PathLog.debug("processing the whole job base object") @@ -137,36 +222,40 @@ class ObjectPocket(PathPocketBase.ObjectPocket): if obj.ProcessStockArea is True: job = PathUtils.findParentJob(obj) - stockEnvShape = PathUtils.getEnvelope(job.Stock.Shape, subshape=None, depthparams=self.depthparams) + stockEnvShape = PathUtils.getEnvelope( + job.Stock.Shape, subshape=None, depthparams=self.depthparams + ) obj.removalshape = stockEnvShape.cut(base.Shape) # obj.removalshape.tessellate(0.1) else: - env = PathUtils.getEnvelope(base.Shape, subshape=None, depthparams=self.depthparams) + env = PathUtils.getEnvelope( + base.Shape, subshape=None, depthparams=self.depthparams + ) obj.removalshape = env.cut(base.Shape) # obj.removalshape.tessellate(0.1) - removalshapes.append((obj.removalshape, False, '3DPocket')) + removalshapes.append((obj.removalshape, False, "3DPocket")) return removalshapes def areaOpSetDefaultValues(self, obj, job): - '''areaOpSetDefaultValues(obj, job) ... set default values''' + """areaOpSetDefaultValues(obj, job) ... set default values""" obj.StepOver = 100 obj.ZigZagAngle = 45 - obj.HandleMultipleFeatures = 'Collectively' + obj.HandleMultipleFeatures = "Collectively" obj.AdaptivePocketStart = False obj.AdaptivePocketFinish = False obj.ProcessStockArea = False # methods for eliminating air milling with some pockets: adpative start and finish def calculateAdaptivePocket(self, obj, base, subObjTups): - '''calculateAdaptivePocket(obj, base, subObjTups) + """calculateAdaptivePocket(obj, base, subObjTups) Orient multiple faces around common facial center of mass. Identify edges that are connections for adjacent faces. Attempt to separate unconnected edges into top and bottom loops of the pocket. Trim the top and bottom of the pocket if available and requested. - return: tuple with pocket shape information''' + return: tuple with pocket shape information""" low = [] high = [] removeList = [] @@ -203,23 +292,27 @@ class ObjectPocket(PathPocketBase.ObjectPocket): highFaceShape = Part.Face(Part.Wire(Part.__sortEdges__(allEdges))) except Exception as ee: PathLog.warning(ee) - PathLog.error(translate("Path", "A planar adaptive start is unavailable. The non-planar will be attempted.")) + PathLog.error( + "A planar adaptive start is unavailable. The non-planar will be attempted." + ) tryNonPlanar = True else: makeHighFace = 1 if tryNonPlanar is True: try: - highFaceShape = Part.makeFilledFace(Part.__sortEdges__(allEdges)) # NON-planar face method + highFaceShape = Part.makeFilledFace( + Part.__sortEdges__(allEdges) + ) # NON-planar face method except Exception as eee: PathLog.warning(eee) - PathLog.error(translate("Path", "The non-planar adaptive start is also unavailable.") + "(1)") + PathLog.error("The non-planar adaptive start is also unavailable.") isHighFacePlanar = False else: makeHighFace = 2 if makeHighFace > 0: - FreeCAD.ActiveDocument.addObject('Part::Feature', 'topEdgeFace') + FreeCAD.ActiveDocument.addObject("Part::Feature", "topEdgeFace") highFace = FreeCAD.ActiveDocument.ActiveObject highFace.Shape = highFaceShape removeList.append(highFace.Name) @@ -228,9 +321,17 @@ class ObjectPocket(PathPocketBase.ObjectPocket): if makeHighFace == 2: mx = hzmax + obj.StepDown.Value mn = hzmin - obj.StepDown.Value - if highFace.Shape.BoundBox.ZMax > mx or highFace.Shape.BoundBox.ZMin < mn: - PathLog.warning("ZMaxDiff: {}; ZMinDiff: {}".format(highFace.Shape.BoundBox.ZMax - mx, highFace.Shape.BoundBox.ZMin - mn)) - PathLog.error(translate("Path", "The non-planar adaptive start is also unavailable.") + "(2)") + if ( + highFace.Shape.BoundBox.ZMax > mx + or highFace.Shape.BoundBox.ZMin < mn + ): + PathLog.warning( + "ZMaxDiff: {}; ZMinDiff: {}".format( + highFace.Shape.BoundBox.ZMax - mx, + highFace.Shape.BoundBox.ZMin - mn, + ) + ) + PathLog.error("The non-planar adaptive start is also unavailable.") isHighFacePlanar = False makeHighFace = 0 else: @@ -252,7 +353,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): PathLog.error("An adaptive finish is unavailable.") isLowFacePlanar = False else: - FreeCAD.ActiveDocument.addObject('Part::Feature', 'bottomEdgeFace') + FreeCAD.ActiveDocument.addObject("Part::Feature", "bottomEdgeFace") lowFace = FreeCAD.ActiveDocument.ActiveObject lowFace.Shape = lowFaceShape removeList.append(lowFace.Name) @@ -279,9 +380,12 @@ class ObjectPocket(PathPocketBase.ObjectPocket): step_down=obj.StepDown.Value, z_finish_step=finish_step, final_depth=finDep, - user_depths=None) + user_depths=None, + ) shape = Part.makeCompound(Faces) - env = PathUtils.getEnvelope(base[0].Shape, subshape=shape, depthparams=depthparams) + env = PathUtils.getEnvelope( + base[0].Shape, subshape=shape, depthparams=depthparams + ) cuts.append(env.cut(base[0].Shape)) # Might need to change to .cut(job.Stock.Shape) if pocket has no bottom @@ -305,8 +409,11 @@ class ObjectPocket(PathPocketBase.ObjectPocket): step_down=obj.StepDown.Value, z_finish_step=finish_step, final_depth=finDep1, - user_depths=None) - envTop = PathUtils.getEnvelope(base[0].Shape, subshape=highFace.Shape, depthparams=depthparams1) + user_depths=None, + ) + envTop = PathUtils.getEnvelope( + base[0].Shape, subshape=highFace.Shape, depthparams=depthparams1 + ) cbi = len(cuts) - 1 cuts.append(cuts[cbi].cut(envTop)) @@ -326,18 +433,20 @@ class ObjectPocket(PathPocketBase.ObjectPocket): step_down=obj.StepDown.Value, z_finish_step=finish_step, final_depth=finDep2, - user_depths=None) - envBottom = PathUtils.getEnvelope(base[0].Shape, subshape=lowFace.Shape, depthparams=depthparams2) + user_depths=None, + ) + envBottom = PathUtils.getEnvelope( + base[0].Shape, subshape=lowFace.Shape, depthparams=depthparams2 + ) cbi = len(cuts) - 1 cuts.append(cuts[cbi].cut(envBottom)) # package pocket details into tuple - sdi = len(starts) - 1 - fdi = len(finals) - 1 cbi = len(cuts) - 1 - pocket = (cuts[cbi], False, '3DPocket') + pocket = (cuts[cbi], False, "3DPocket") if FreeCAD.GuiUp: import FreeCADGui + for rn in removeList: FreeCADGui.ActiveDocument.getObject(rn).Visibility = False @@ -347,11 +456,12 @@ class ObjectPocket(PathPocketBase.ObjectPocket): return pocket def orderFacesAroundCenterOfMass(self, subObjTups): - '''orderFacesAroundCenterOfMass(subObjTups) + """orderFacesAroundCenterOfMass(subObjTups) Order list of faces by center of mass in angular order around average center of mass for all faces. Positive X-axis is zero degrees. - return: subObjTups [ordered/sorted]''' + return: subObjTups [ordered/sorted]""" import math + newList = [] vectList = [] comList = [] @@ -364,7 +474,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): return vectItem[3] def getFaceIdx(sub): - return int(sub.replace('Face', '')) - 1 + return int(sub.replace("Face", "")) - 1 # get CenterOfMass for each face and add to sumCenterOfMass for average calculation for (sub, face) in subObjTups: @@ -382,22 +492,26 @@ class ObjectPocket(PathPocketBase.ObjectPocket): # calculate vector (mag, direct) for each face from avgCom for (sub, face, com) in comList: - adjCom = com.sub(avgCom) # effectively treats avgCom as origin for each face. - mag = math.sqrt(adjCom.x**2 + adjCom.y**2) # adjCom.Length without Z values + adjCom = com.sub( + avgCom + ) # effectively treats avgCom as origin for each face. + mag = math.sqrt( + adjCom.x ** 2 + adjCom.y ** 2 + ) # adjCom.Length without Z values drctn = 0.0 # Determine direction of vector if adjCom.x > 0.0: if adjCom.y > 0.0: # Q1 - drctn = math.degrees(math.atan(adjCom.y/adjCom.x)) + drctn = math.degrees(math.atan(adjCom.y / adjCom.x)) elif adjCom.y < 0.0: - drctn = -math.degrees(math.atan(adjCom.x/adjCom.y)) + 270.0 + drctn = -math.degrees(math.atan(adjCom.x / adjCom.y)) + 270.0 elif adjCom.y == 0.0: drctn = 0.0 elif adjCom.x < 0.0: if adjCom.y < 0.0: - drctn = math.degrees(math.atan(adjCom.y/adjCom.x)) + 180.0 + drctn = math.degrees(math.atan(adjCom.y / adjCom.x)) + 180.0 elif adjCom.y > 0.0: - drctn = -math.degrees(math.atan(adjCom.x/adjCom.y)) + 90.0 + drctn = -math.degrees(math.atan(adjCom.x / adjCom.y)) + 90.0 elif adjCom.y == 0.0: drctn = 180.0 elif adjCom.x == 0.0: @@ -434,8 +548,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket): return newList def findSharedEdges(self, subObjTups): - '''findSharedEdges(self, subObjTups) - Find connected edges given a group of faces''' + """findSharedEdges(self, subObjTups) + Find connected edges given a group of faces""" checkoutList = [] searchedList = [] shared = [] @@ -471,7 +585,11 @@ class ObjectPocket(PathPocketBase.ObjectPocket): for ei2 in range(0, len(face2.Edges)): edg2 = face2.Edges[ei2] if edg1.isSame(edg2) is True: - PathLog.debug("{}.Edges[{}] connects at {}.Edges[{}]".format(sub1, ei1, sub2, ei2)) + PathLog.debug( + "{}.Edges[{}] connects at {}.Edges[{}]".format( + sub1, ei1, sub2, ei2 + ) + ) shared.append((sub1, face1, ei1)) touching[sub1].append(ei1) touching[sub2].append(ei2) @@ -486,8 +604,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket): return (shared, touchingCleaned) def identifyUnconnectedEdges(self, subObjTups, touching): - '''identifyUnconnectedEdges(subObjTups, touching) - Categorize unconnected edges into two groups, if possible: low and high''' + """identifyUnconnectedEdges(subObjTups, touching) + Categorize unconnected edges into two groups, if possible: low and high""" # Identify unconnected edges # (should be top edge loop if all faces form loop with bottom face(s) included) high = [] @@ -518,7 +636,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): com = FreeCAD.Vector(0, 0, 0) com.add(edg0.CenterOfMass) com.add(edg1.CenterOfMass) - avgCom = FreeCAD.Vector(com.x/2.0, com.y/2.0, com.z/2.0) + avgCom = FreeCAD.Vector(com.x / 2.0, com.y / 2.0, com.z / 2.0) if avgCom.z > face.CenterOfMass.z: high.extend(holding) else: @@ -534,9 +652,9 @@ class ObjectPocket(PathPocketBase.ObjectPocket): return (low, high) def hasCommonVertex(self, edge1, edge2, show=False): - '''findCommonVertexIndexes(edge1, edge2, show=False) + """findCommonVertexIndexes(edge1, edge2, show=False) Compare vertexes of two edges to identify a common vertex. - Returns the vertex index of edge1 to which edge2 is connected''' + Returns the vertex index of edge1 to which edge2 is connected""" if show is True: PathLog.info("New findCommonVertex()... ") @@ -561,9 +679,9 @@ class ObjectPocket(PathPocketBase.ObjectPocket): return -1 def groupConnectedEdges(self, holding): - '''groupConnectedEdges(self, holding) + """groupConnectedEdges(self, holding) Take edges and determine which are connected. - Group connected chains/loops into: low and high''' + Group connected chains/loops into: low and high""" holds = [] grps = [] searched = [] @@ -607,7 +725,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): while len(holds) > 0: if loops > 500: - PathLog.error('BREAK --- LOOPS LIMIT of 500 ---') + PathLog.error("BREAK --- LOOPS LIMIT of 500 ---") break save = False @@ -701,8 +819,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket): return (low, high) def getMinMaxOfFaces(self, Faces): - '''getMinMaxOfFaces(Faces) - return the zmin and zmax values for given set of faces or edges.''' + """getMinMaxOfFaces(Faces) + return the zmin and zmax values for given set of faces or edges.""" zmin = Faces[0].BoundBox.ZMax zmax = Faces[0].BoundBox.ZMin for f in Faces: @@ -718,7 +836,7 @@ def SetupProperties(): def Create(name, obj=None, parentJob=None): - '''Create(name) ... Creates and returns a Pocket operation.''' + """Create(name) ... Creates and returns a Pocket operation.""" if obj is None: obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) obj.Proxy = ObjectPocket(obj, name, parentJob) diff --git a/src/Mod/Path/PathScripts/PathPocketBase.py b/src/Mod/Path/PathScripts/PathPocketBase.py index 9563470a25..9a0b111165 100644 --- a/src/Mod/Path/PathScripts/PathPocketBase.py +++ b/src/Mod/Path/PathScripts/PathPocketBase.py @@ -21,65 +21,164 @@ # * * # *************************************************************************** +import FreeCAD +from PySide.QtCore import QT_TRANSLATE_NOOP import PathScripts.PathAreaOp as PathAreaOp import PathScripts.PathLog as PathLog import PathScripts.PathOp as PathOp -from PySide import QtCore __title__ = "Base Path Pocket Operation" __author__ = "sliptonic (Brad Collette)" __url__ = "https://www.freecadweb.org" __doc__ = "Base class and implementation for Path pocket operations." -PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -# PathLog.trackModule(PathLog.thisModule()) +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) - -# Qt translation handling -def translate(context, text, disambig=None): - return QtCore.QCoreApplication.translate(context, text, disambig) +translate = FreeCAD.Qt.translate class ObjectPocket(PathAreaOp.ObjectOp): - '''Base class for proxy objects of all pocket operations.''' + """Base class for proxy objects of all pocket operations.""" + + @classmethod + def pocketPropertyEnumerations(self, dataType="data"): + """helixOpPropertyEnumerations(dataType="data")... return property enumeration lists of specified dataType. + Args: + dataType = 'data', 'raw', 'translated' + Notes: + 'data' is list of internal string literals used in code + 'raw' is list of (translated_text, data_string) tuples + 'translated' is list of translated string literals + """ + + enums = { + "CutMode": [ + (translate("Path_Pocket", "Climb"), "Climb"), + (translate("Path_Pocket", "Conventional"), "Conventional"), + ], # this is the direction that the profile runs + "StartAt": [ + (translate("Path_Pocket", "Center"), "Center"), + (translate("Path_Pocket", "Edge"), "Edge"), + ], + "OffsetPattern": [ + (translate("Path_Pocket", "ZigZag"), "ZigZag"), + (translate("Path_Pocket", "Offset"), "Offset"), + (translate("Path_Pocket", "Spiral"), "Spiral"), + (translate("Path_Pocket", "ZigZagOffset"), "ZigZagOffset"), + (translate("Path_Pocket", "Line"), "Line"), + (translate("Path_Pocket", "Grid"), "Grid"), + (translate("Path_Pocket", "Triangle"), "Triangle"), + ], # Fill Pattern + } + + if dataType == "raw": + return enums + + data = list() + idx = 0 if dataType == "translated" else 1 + + PathLog.debug(enums) + + for k, v in enumerate(enums): + data.append((v, [tup[idx] for tup in enums[v]])) + PathLog.debug(data) + + return data def areaOpFeatures(self, obj): - '''areaOpFeatures(obj) ... Pockets have a FinishDepth and work on Faces''' - return PathOp.FeatureBaseFaces | PathOp.FeatureFinishDepth | self.pocketOpFeatures(obj) + """areaOpFeatures(obj) ... Pockets have a FinishDepth and work on Faces""" + return ( + PathOp.FeatureBaseFaces + | PathOp.FeatureFinishDepth + | self.pocketOpFeatures(obj) + ) def pocketOpFeatures(self, obj): # pylint: disable=unused-argument return 0 def initPocketOp(self, obj): - '''initPocketOp(obj) ... overwrite to initialize subclass. - Can safely be overwritten by subclass.''' - pass # pylint: disable=unnecessary-pass + """initPocketOp(obj) ... overwrite to initialize subclass. + Can safely be overwritten by subclass.""" + pass # pylint: disable=unnecessary-pass def pocketInvertExtraOffset(self): - '''pocketInvertExtraOffset() ... return True if ExtraOffset's direction is inward. - Can safely be overwritten by subclass.''' + """pocketInvertExtraOffset() ... return True if ExtraOffset's direction is inward. + Can safely be overwritten by subclass.""" return False def initAreaOp(self, obj): - '''initAreaOp(obj) ... create pocket specific properties. - Do not overwrite, implement initPocketOp(obj) instead.''' + """initAreaOp(obj) ... create pocket specific properties. + Do not overwrite, implement initPocketOp(obj) instead.""" PathLog.track() # Pocket Properties - obj.addProperty("App::PropertyEnumeration", "CutMode", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise (CW) or CounterClockWise (CCW)")) - obj.addProperty("App::PropertyDistance", "ExtraOffset", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Extra offset to apply to the operation. Direction is operation dependent.")) - obj.addProperty("App::PropertyEnumeration", "StartAt", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Start pocketing at center or boundary")) - obj.addProperty("App::PropertyPercent", "StepOver", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Percent of cutter diameter to step over on each pass")) - obj.addProperty("App::PropertyFloat", "ZigZagAngle", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Angle of the zigzag pattern")) - obj.addProperty("App::PropertyEnumeration", "OffsetPattern", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "Clearing pattern to use")) - obj.addProperty("App::PropertyBool", "MinTravel", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Use 3D Sorting of Path")) - obj.addProperty("App::PropertyBool", "KeepToolDown", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Attempts to avoid unnecessary retractions.")) + obj.addProperty( + "App::PropertyEnumeration", + "CutMode", + "Pocket", + QT_TRANSLATE_NOOP( + "App::Property", + "The direction that the toolpath should go around the part ClockWise (CW) or CounterClockWise (CCW)", + ), + ) + obj.addProperty( + "App::PropertyDistance", + "ExtraOffset", + "Pocket", + QT_TRANSLATE_NOOP( + "App::Property", + "Extra offset to apply to the operation. Direction is operation dependent.", + ), + ) + obj.addProperty( + "App::PropertyEnumeration", + "StartAt", + "Pocket", + QT_TRANSLATE_NOOP("App::Property", "Start pocketing at center or boundary"), + ) + obj.addProperty( + "App::PropertyPercent", + "StepOver", + "Pocket", + QT_TRANSLATE_NOOP( + "App::Property", "Percent of cutter diameter to step over on each pass" + ), + ) + obj.addProperty( + "App::PropertyFloat", + "ZigZagAngle", + "Pocket", + QT_TRANSLATE_NOOP("App::Property", "Angle of the zigzag pattern"), + ) + obj.addProperty( + "App::PropertyEnumeration", + "OffsetPattern", + "Face", + QT_TRANSLATE_NOOP("App::Property", "Clearing pattern to use"), + ) + obj.addProperty( + "App::PropertyBool", + "MinTravel", + "Pocket", + QT_TRANSLATE_NOOP("App::Property", "Use 3D Sorting of Path"), + ) + obj.addProperty( + "App::PropertyBool", + "KeepToolDown", + "Pocket", + QT_TRANSLATE_NOOP( + "App::Property", "Attempts to avoid unnecessary retractions." + ), + ) - obj.CutMode = ['Climb', 'Conventional'] - obj.StartAt = ['Center', 'Edge'] - obj.OffsetPattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle'] + for n in self.pocketPropertyEnumerations(): + setattr(obj, n[0], n[1]) self.initPocketOp(obj) @@ -88,40 +187,48 @@ class ObjectPocket(PathAreaOp.ObjectOp): return not obj.KeepToolDown def areaOpUseProjection(self, obj): - '''areaOpUseProjection(obj) ... return False''' + """areaOpUseProjection(obj) ... return False""" return False def areaOpAreaParams(self, obj, isHole): - '''areaOpAreaParams(obj, isHole) ... return dictionary with pocket's area parameters''' + """areaOpAreaParams(obj, isHole) ... return dictionary with pocket's area parameters""" params = {} - params['Fill'] = 0 - params['Coplanar'] = 0 - params['PocketMode'] = 1 - params['SectionCount'] = -1 - params['Angle'] = obj.ZigZagAngle - params['FromCenter'] = (obj.StartAt == "Center") - params['PocketStepover'] = (self.radius * 2) * (float(obj.StepOver)/100) + params["Fill"] = 0 + params["Coplanar"] = 0 + params["PocketMode"] = 1 + params["SectionCount"] = -1 + params["Angle"] = obj.ZigZagAngle + params["FromCenter"] = obj.StartAt == "Center" + params["PocketStepover"] = (self.radius * 2) * (float(obj.StepOver) / 100) extraOffset = obj.ExtraOffset.Value if self.pocketInvertExtraOffset(): extraOffset = 0 - extraOffset - params['PocketExtraOffset'] = extraOffset - params['ToolRadius'] = self.radius + params["PocketExtraOffset"] = extraOffset + params["ToolRadius"] = self.radius - Pattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle'] - params['PocketMode'] = Pattern.index(obj.OffsetPattern) + 1 + Pattern = [ + "ZigZag", + "Offset", + "Spiral", + "ZigZagOffset", + "Line", + "Grid", + "Triangle", + ] + params["PocketMode"] = Pattern.index(obj.OffsetPattern) + 1 if obj.SplitArcs: - params['Explode'] = True - params['FitArcs'] = False + params["Explode"] = True + params["FitArcs"] = False return params def areaOpPathParams(self, obj, isHole): - '''areaOpAreaParams(obj, isHole) ... return dictionary with pocket's path parameters''' + """areaOpAreaParams(obj, isHole) ... return dictionary with pocket's path parameters""" params = {} - CutMode = ['Conventional', 'Climb'] - params['orientation'] = CutMode.index(obj.CutMode) + CutMode = ["Conventional", "Climb"] + params["orientation"] = CutMode.index(obj.CutMode) # if MinTravel is turned on, set path sorting to 3DSort # 3DSort shouldn't be used without a valid start point. Can cause @@ -133,19 +240,19 @@ class ObjectPocket(PathAreaOp.ObjectOp): # any problem # if obj.MinTravel and obj.UseStartPoint and obj.StartPoint is not None: - params['sort_mode'] = 3 - params['threshold'] = self.radius * 2 + params["sort_mode"] = 3 + params["threshold"] = self.radius * 2 return params def SetupProperties(): setup = PathAreaOp.SetupProperties() - setup.append('CutMode') - setup.append('ExtraOffset') - setup.append('StepOver') - setup.append('ZigZagAngle') - setup.append('OffsetPattern') - setup.append('StartAt') - setup.append('MinTravel') - setup.append('KeepToolDown') + setup.append("CutMode") + setup.append("ExtraOffset") + setup.append("StepOver") + setup.append("ZigZagAngle") + setup.append("OffsetPattern") + setup.append("StartAt") + setup.append("MinTravel") + setup.append("KeepToolDown") return setup diff --git a/src/Mod/Path/PathScripts/PathPocketBaseGui.py b/src/Mod/Path/PathScripts/PathPocketBaseGui.py index abb39c5f26..781800bdb2 100644 --- a/src/Mod/Path/PathScripts/PathPocketBaseGui.py +++ b/src/Mod/Path/PathScripts/PathPocketBaseGui.py @@ -22,50 +22,69 @@ import FreeCAD import FreeCADGui -import PathGui as PGui # ensure Path/Gui/Resources are loaded +import PathGui as PGui # ensure Path/Gui/Resources are loaded import PathScripts.PathGui as PathGui import PathScripts.PathOpGui as PathOpGui - -from PySide import QtCore #, QtGui +import PathScripts.PathPocket as PathPocket +import PathScripts.PathLog as PathLog __title__ = "Path Pocket Base Operation UI" __author__ = "sliptonic (Brad Collette)" __url__ = "https://www.freecadweb.org" __doc__ = "Base page controller and command implementation for path pocket operations." -def translate(context, text, disambig=None): - return QtCore.QCoreApplication.translate(context, text, disambig) +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) + +translate = FreeCAD.Qt.translate + +FeaturePocket = 0x01 +FeatureFacing = 0x02 +FeatureOutline = 0x04 -FeaturePocket = 0x01 -FeatureFacing = 0x02 -FeatureOutline = 0x04 class TaskPanelOpPage(PathOpGui.TaskPanelPage): - '''Page controller class for pocket operations, supports: - FeaturePocket ... used for pocketing operation - FeatureFacing ... used for face milling operation - FeatureOutline ... used for pocket-shape operation - ''' + """Page controller class for pocket operations, supports: + FeaturePocket ... used for pocketing operation + FeatureFacing ... used for face milling operation + FeatureOutline ... used for pocket-shape operation + """ def pocketFeatures(self): - '''pocketFeatures() ... return which features of the UI are supported by the operation. + """pocketFeatures() ... return which features of the UI are supported by the operation. FeaturePocket ... used for pocketing operation FeatureFacing ... used for face milling operation FeatureOutline ... used for pocket-shape operation - Must be overwritten by subclasses''' - pass # pylint: disable=unnecessary-pass + Must be overwritten by subclasses""" + pass # pylint: disable=unnecessary-pass def getForm(self): - '''getForm() ... returns UI, adapted to the results from pocketFeatures()''' + """getForm() ... returns UI, adapted to the results from pocketFeatures()""" form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpPocketFullEdit.ui") + comboToPropertyMap = [ + ("cutMode", "CutMode"), + ("offsetPattern", "OffsetPattern"), + ] + enumTups = PathPocket.ObjectPocket.pocketPropertyEnumerations(dataType="raw") + + self.populateCombobox(form, enumTups, comboToPropertyMap) + if not FeatureFacing & self.pocketFeatures(): form.facingWidget.hide() form.clearEdges.hide() if FeaturePocket & self.pocketFeatures(): form.extraOffset_label.setText(translate("PathPocket", "Pass Extension")) - form.extraOffset.setToolTip(translate("PathPocket", "The distance the facing operation will extend beyond the boundary shape.")) + form.extraOffset.setToolTip( + translate( + "PathPocket", + "The distance the facing operation will extend beyond the boundary shape.", + ) + ) if not (FeatureOutline & self.pocketFeatures()): form.useOutline.hide() @@ -76,6 +95,21 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): return form + def populateCombobox(self, form, enumTups, comboBoxesPropertyMap): + """fillComboboxes(form, comboBoxesPropertyMap) ... populate comboboxes with translated enumerations + ** comboBoxesPropertyMap will be unnecessary if UI files use strict combobox naming protocol. + Args: + form = UI form + enumTups = list of (translated_text, data_string) tuples + comboBoxesPropertyMap = list of (translated_text, data_string) tuples + """ + # Load appropriate enumerations in each combobox + for cb, prop in comboBoxesPropertyMap: + box = getattr(form, cb) # Get the combobox + box.clear() # clear the combobox + for text, data in enumTups[prop]: # load enumerations + box.addItem(text, data) + def updateMinTravel(self, obj, setModel=True): if obj.UseStartPoint: self.form.minTravel.setEnabled(True) @@ -87,24 +121,24 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): obj.MinTravel = self.form.minTravel.isChecked() def updateZigZagAngle(self, obj, setModel=True): - if obj.OffsetPattern in ['Offset', 'Spiral']: + if obj.OffsetPattern in ["Offset", "Spiral"]: self.form.zigZagAngle.setEnabled(False) else: self.form.zigZagAngle.setEnabled(True) if setModel: - PathGui.updateInputField(obj, 'ZigZagAngle', self.form.zigZagAngle) + PathGui.updateInputField(obj, "ZigZagAngle", self.form.zigZagAngle) def getFields(self, obj): - '''getFields(obj) ... transfers values from UI to obj's proprties''' - if obj.CutMode != str(self.form.cutMode.currentText()): - obj.CutMode = str(self.form.cutMode.currentText()) + """getFields(obj) ... transfers values from UI to obj's proprties""" + if obj.CutMode != str(self.form.cutMode.currentData()): + obj.CutMode = str(self.form.cutMode.currentData()) if obj.StepOver != self.form.stepOverPercent.value(): obj.StepOver = self.form.stepOverPercent.value() - if obj.OffsetPattern != str(self.form.offsetPattern.currentText()): - obj.OffsetPattern = str(self.form.offsetPattern.currentText()) + if obj.OffsetPattern != str(self.form.offsetPattern.currentData()): + obj.OffsetPattern = str(self.form.offsetPattern.currentData()) - PathGui.updateInputField(obj, 'ExtraOffset', self.form.extraOffset) + PathGui.updateInputField(obj, "ExtraOffset", self.form.extraOffset) self.updateToolController(obj, self.form.toolController) self.updateCoolant(obj, self.form.coolantController) self.updateZigZagAngle(obj) @@ -119,20 +153,29 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): self.updateMinTravel(obj) if FeatureFacing & self.pocketFeatures(): - if obj.BoundaryShape != str(self.form.boundaryShape.currentText()): - obj.BoundaryShape = str(self.form.boundaryShape.currentText()) + print(obj.BoundaryShape) + print(self.form.boundaryShape.currentText()) + print(self.form.boundaryShape.currentData()) + if obj.BoundaryShape != str(self.form.boundaryShape.currentData()): + obj.BoundaryShape = str(self.form.boundaryShape.currentData()) if obj.ClearEdges != self.form.clearEdges.isChecked(): obj.ClearEdges = self.form.clearEdges.isChecked() def setFields(self, obj): - '''setFields(obj) ... transfers obj's property values to UI''' + """setFields(obj) ... transfers obj's property values to UI""" self.form.stepOverPercent.setValue(obj.StepOver) - self.form.extraOffset.setText(FreeCAD.Units.Quantity(obj.ExtraOffset.Value, FreeCAD.Units.Length).UserString) + self.form.extraOffset.setText( + FreeCAD.Units.Quantity( + obj.ExtraOffset.Value, FreeCAD.Units.Length + ).UserString + ) self.form.useStartPoint.setChecked(obj.UseStartPoint) if FeatureOutline & self.pocketFeatures(): self.form.useOutline.setChecked(obj.UseOutline) - self.form.zigZagAngle.setText(FreeCAD.Units.Quantity(obj.ZigZagAngle, FreeCAD.Units.Angle).UserString) + self.form.zigZagAngle.setText( + FreeCAD.Units.Quantity(obj.ZigZagAngle, FreeCAD.Units.Angle).UserString + ) self.updateZigZagAngle(obj, False) self.form.minTravel.setChecked(obj.MinTravel) @@ -148,7 +191,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage): self.form.clearEdges.setChecked(obj.ClearEdges) def getSignalsForUpdate(self, obj): - '''getSignalsForUpdate(obj) ... return list of signals for updating obj''' + """getSignalsForUpdate(obj) ... return list of signals for updating obj""" signals = [] signals.append(self.form.cutMode.currentIndexChanged) diff --git a/src/Mod/Path/PathScripts/PathPocketGui.py b/src/Mod/Path/PathScripts/PathPocketGui.py index a452e5a838..80b20ef9ab 100644 --- a/src/Mod/Path/PathScripts/PathPocketGui.py +++ b/src/Mod/Path/PathScripts/PathPocketGui.py @@ -24,27 +24,41 @@ import FreeCAD import PathScripts.PathOpGui as PathOpGui import PathScripts.PathPocket as PathPocket import PathScripts.PathPocketBaseGui as PathPocketBaseGui +import PathScripts.PathLog as PathLog + +from PySide.QtCore import QT_TRANSLATE_NOOP + +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -from PySide import QtCore __title__ = "Path Pocket Operation UI" __author__ = "sliptonic (Brad Collette)" __url__ = "https://www.freecadweb.org" __doc__ = "Pocket operation page controller and command implementation." + class TaskPanelOpPage(PathPocketBaseGui.TaskPanelOpPage): - '''Page controller class for Pocket operation''' + """Page controller class for Pocket operation""" def pocketFeatures(self): - '''pocketFeatures() ... return FeaturePocket (see PathPocketBaseGui)''' + """pocketFeatures() ... return FeaturePocket (see PathPocketBaseGui)""" return PathPocketBaseGui.FeaturePocket -Command = PathOpGui.SetupOperation('Pocket 3D', - PathPocket.Create, - TaskPanelOpPage, - 'Path_3DPocket', - QtCore.QT_TRANSLATE_NOOP("Path_Pocket", "3D Pocket"), - QtCore.QT_TRANSLATE_NOOP("Path_Pocket", "Creates a Path 3D Pocket object from a face or faces"), - PathPocket.SetupProperties) + +Command = PathOpGui.SetupOperation( + "Pocket3D", + PathPocket.Create, + TaskPanelOpPage, + "Path_3DPocket", + QT_TRANSLATE_NOOP("Path_Pocket3D", "3D Pocket"), + QT_TRANSLATE_NOOP( + "Path_Pocket3D", "Creates a Path 3D Pocket object from a face or faces" + ), + PathPocket.SetupProperties, +) FreeCAD.Console.PrintLog("Loading PathPocketGui... done\n") diff --git a/src/Mod/Path/PathScripts/PathPocketShape.py b/src/Mod/Path/PathScripts/PathPocketShape.py index 87ac211fe8..12cbc1c046 100644 --- a/src/Mod/Path/PathScripts/PathPocketShape.py +++ b/src/Mod/Path/PathScripts/PathPocketShape.py @@ -20,13 +20,13 @@ # * * # *************************************************************************** +from PySide.QtCore import QT_TRANSLATE_NOOP import FreeCAD import PathScripts.PathGeom as PathGeom import PathScripts.PathLog as PathLog import PathScripts.PathOp as PathOp import PathScripts.PathPocketBase as PathPocketBase -from PySide import QtCore # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader @@ -46,13 +46,11 @@ __url__ = "https://www.freecadweb.org" __doc__ = "Class and implementation of shape based Pocket operation." -PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -# PathLog.trackModule(PathLog.thisModule()) - - -# Qt translation handling -def translate(context, text, disambig=None): - return QtCore.QCoreApplication.translate(context, text, disambig) +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) class ObjectPocket(PathPocketBase.ObjectPocket): @@ -68,8 +66,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket): "App::PropertyBool", "UseOutline", "Pocket", - QtCore.QT_TRANSLATE_NOOP( - "PathPocketShape", "Uses the outline of the base geometry." + QT_TRANSLATE_NOOP( + "App::Property", "Uses the outline of the base geometry." ), ) @@ -113,10 +111,9 @@ class ObjectPocket(PathPocketBase.ObjectPocket): if "Face" in sub: if sub not in avoidFeatures and not self.clasifySub(base, sub): PathLog.error( - translate( - "PathPocket", "Pocket does not support shape %s.%s" + "Pocket does not support shape {}.{}".format( + base.Label, sub ) - % (base.Label, sub) ) # Convert horizontal faces to use outline only if requested @@ -136,12 +133,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): face = Part.Face(w) # face.tessellate(0.1) if PathGeom.isRoughly(face.Area, 0): - PathLog.error( - translate( - "PathPocket", - "Vertical faces do not form a loop - ignoring", - ) - ) + PathLog.error("Vertical faces do not form a loop - ignoring") else: self.horiz.append(face) @@ -271,11 +263,7 @@ class ObjectPocket(PathPocketBase.ObjectPocket): self.vert.append(face) return True else: - PathLog.error( - translate( - "Path", "Failed to identify vertical face from {}.".format(sub) - ) - ) + PathLog.error("Failed to identify vertical face from {}".format(sub)) else: PathLog.debug(" -type(face.Surface): {}".format(type(face.Surface))) diff --git a/src/Mod/Path/PathScripts/PathPocketShapeGui.py b/src/Mod/Path/PathScripts/PathPocketShapeGui.py index a0f73d32c0..9f88dc128c 100644 --- a/src/Mod/Path/PathScripts/PathPocketShapeGui.py +++ b/src/Mod/Path/PathScripts/PathPocketShapeGui.py @@ -26,42 +26,52 @@ import PathScripts.PathOpGui as PathOpGui import PathScripts.PathPocketShape as PathPocketShape import PathScripts.PathPocketBaseGui as PathPocketBaseGui import PathScripts.PathFeatureExtensionsGui as PathFeatureExtensionsGui - -from PySide import QtCore +from PySide.QtCore import QT_TRANSLATE_NOOP # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader -Part = LazyLoader('Part', globals(), 'Part') + +Part = LazyLoader("Part", globals(), "Part") __title__ = "Path Pocket Shape Operation UI" __author__ = "sliptonic (Brad Collette)" __url__ = "https://www.freecadweb.org" __doc__ = "Pocket Shape operation page controller and command implementation." -def translate(context, text, disambig=None): - return QtCore.QCoreApplication.translate(context, text, disambig) +if False: + PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) + PathLog.trackModule(PathLog.thisModule()) +else: + PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) + +translate = FreeCAD.Qt.translate -PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) class TaskPanelOpPage(PathPocketBaseGui.TaskPanelOpPage): - '''Page controller class for Pocket operation''' + """Page controller class for Pocket operation""" def pocketFeatures(self): - '''pocketFeatures() ... return FeaturePocket (see PathPocketBaseGui)''' + """pocketFeatures() ... return FeaturePocket (see PathPocketBaseGui)""" return PathPocketBaseGui.FeaturePocket | PathPocketBaseGui.FeatureOutline def taskPanelBaseLocationPage(self, obj, features): - if not hasattr(self, 'extensionsPanel'): - self.extensionsPanel = PathFeatureExtensionsGui.TaskPanelExtensionPage(obj, features) # pylint: disable=attribute-defined-outside-init + if not hasattr(self, "extensionsPanel"): + self.extensionsPanel = PathFeatureExtensionsGui.TaskPanelExtensionPage( + obj, features + ) # pylint: disable=attribute-defined-outside-init return self.extensionsPanel -Command = PathOpGui.SetupOperation('Pocket Shape', - PathPocketShape.Create, - TaskPanelOpPage, - 'Path_Pocket', - QtCore.QT_TRANSLATE_NOOP("Path_Pocket", "Pocket Shape"), - QtCore.QT_TRANSLATE_NOOP("Path_Pocket", "Creates a Path Pocket object from a face or faces"), - PathPocketShape.SetupProperties) + +Command = PathOpGui.SetupOperation( + "Pocket Shape", + PathPocketShape.Create, + TaskPanelOpPage, + "Path_Pocket", + QT_TRANSLATE_NOOP("Path_Pocket_Shape", "Pocket Shape"), + QT_TRANSLATE_NOOP( + "Path_Pocket_Shape", "Creates a Path Pocket object from a face or faces" + ), + PathPocketShape.SetupProperties, +) FreeCAD.Console.PrintLog("Loading PathPocketShapeGui... done\n") diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py index ef561f6361..21da7974e2 100644 --- a/src/Mod/Path/PathScripts/PathSelection.py +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -390,7 +390,7 @@ def select(op): opsel["Helix"] = drillselect opsel["MillFace"] = pocketselect opsel["Pocket"] = pocketselect - opsel["Pocket 3D"] = pocketselect + opsel["Pocket3D"] = pocketselect opsel["Pocket Shape"] = pocketselect opsel["Profile Edges"] = eselect # (depreciated) opsel["Profile Faces"] = fselect # (depreciated)