From 4e8d0cbea386db7b26891560fc4eff9832ba4388 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Sun, 6 Aug 2017 15:48:08 -0700 Subject: [PATCH] Converted MillFace to be based on PathAreaop. --- src/Mod/Path/CMakeLists.txt | 2 + src/Mod/Path/Gui/Resources/Path.qrc | 2 +- ...pPocketEdit.ui => PageOpPocketFullEdit.ui} | 67 +- src/Mod/Path/InitGui.py | 2 +- src/Mod/Path/PathScripts/PathAreaOpGui.py | 2 +- src/Mod/Path/PathScripts/PathMillFace.py | 623 ++---------------- src/Mod/Path/PathScripts/PathMillFaceGui.py | 48 ++ src/Mod/Path/PathScripts/PathPocket.py | 6 +- src/Mod/Path/PathScripts/PathPocketBaseGui.py | 105 +++ src/Mod/Path/PathScripts/PathPocketGui.py | 50 +- src/Mod/Path/PathScripts/PathSelection.py | 1 + 11 files changed, 300 insertions(+), 608 deletions(-) rename src/Mod/Path/Gui/Resources/panels/{PageOpPocketEdit.ui => PageOpPocketFullEdit.ui} (71%) create mode 100644 src/Mod/Path/PathScripts/PathMillFaceGui.py create mode 100644 src/Mod/Path/PathScripts/PathPocketBaseGui.py diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index b7c357052e..e17286f475 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -46,8 +46,10 @@ SET(PathScripts_SRCS PathScripts/PathJob.py PathScripts/PathLog.py PathScripts/PathMillFace.py + PathScripts/PathMillFaceGui.py PathScripts/PathPlane.py PathScripts/PathPocket.py + PathScripts/PathPocketBaseGui.py PathScripts/PathPocketGui.py PathScripts/PathPost.py PathScripts/PathPostProcessor.py diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc index 658a25a142..503542bfe6 100644 --- a/src/Mod/Path/Gui/Resources/Path.qrc +++ b/src/Mod/Path/Gui/Resources/Path.qrc @@ -64,7 +64,7 @@ panels/PageBaseGeometryEdit.ui panels/PageDepthsEdit.ui panels/PageHeightsEdit.ui - panels/PageOpPocketEdit.ui + panels/PageOpPocketFullEdit.ui panels/PageOpProfileFullEdit.ui panels/PocketEdit.ui panels/PointEdit.ui diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpPocketEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui similarity index 71% rename from src/Mod/Path/Gui/Resources/panels/PageOpPocketEdit.ui rename to src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui index 41bec1a885..a2a8e63813 100644 --- a/src/Mod/Path/Gui/Resources/panels/PageOpPocketEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PageOpPocketFullEdit.ui @@ -7,7 +7,7 @@ 0 0 398 - 330 + 454 @@ -36,9 +36,49 @@ + + + + + + + Boundary Shape + + + + + + + + Perimeter + + + + + Boundbox + + + + + + + + Pass Extension + + + + + + + + + + + QFormLayout::AllNonFixedFieldsGrow + @@ -139,17 +179,23 @@ - + + + + + + + Material Allowance - + - + Use Start Point @@ -159,6 +205,19 @@ + + + + Qt::Vertical + + + + 20 + 40 + + + + diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py index 5abd4b9698..6c20ad6665 100644 --- a/src/Mod/Path/InitGui.py +++ b/src/Mod/Path/InitGui.py @@ -62,7 +62,7 @@ class PathWorkbench (Workbench): from PathScripts import PathHop from PathScripts import PathInspect from PathScripts import PathJob - from PathScripts import PathMillFace + from PathScripts import PathMillFaceGui from PathScripts import PathPlane from PathScripts import PathPocketGui from PathScripts import PathPost diff --git a/src/Mod/Path/PathScripts/PathAreaOpGui.py b/src/Mod/Path/PathScripts/PathAreaOpGui.py index f14b377d70..cdb3c0ddb7 100644 --- a/src/Mod/Path/PathScripts/PathAreaOpGui.py +++ b/src/Mod/Path/PathScripts/PathAreaOpGui.py @@ -37,7 +37,7 @@ from PySide import QtCore, QtGui # 0 ... existing toolbox layout # 1 ... reverse order # 2 ... multi panel layout -TaskPanelLayout = 2 +TaskPanelLayout = 0 PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py index ac027da82b..03d97730fa 100644 --- a/src/Mod/Path/PathScripts/PathMillFace.py +++ b/src/Mod/Path/PathScripts/PathMillFace.py @@ -23,15 +23,15 @@ # *************************************************************************** from __future__ import print_function + import FreeCAD -import Path -import PathScripts.PathLog as PathLog -from PySide import QtCore, QtGui -from PathScripts import PathUtils import Part -from PathScripts.PathUtils import waiting_effects -from PathScripts.PathUtils import makeWorkplane -from PathScripts.PathUtils import depth_params +import Path +import PathScripts.PathAreaOp as PathAreaOp +import PathScripts.PathLog as PathLog +import PathScripts.PathUtils as PathUtils + +from PySide import QtCore, QtGui if True: PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) @@ -54,25 +54,12 @@ __url__ = "http://www.freecadweb.org" """Path Face object and FreeCAD command""" -class ObjectFace: +class ObjectFace(PathAreaOp.ObjectOp): - def __init__(self, obj): - obj.addProperty("App::PropertyLinkSubList", "Base", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "The base geometry of this object")) - obj.addProperty("App::PropertyBool", "Active", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "Make False, to prevent operation from generating code")) - obj.addProperty("App::PropertyString", "Comment", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "An optional comment for this profile")) - obj.addProperty("App::PropertyString", "UserLabel", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "User Assigned Label")) + def opFeatures(self, obj): + return PathAreaOp.FeatureTool | PathAreaOp.FeatureDepths | PathAreaOp.FeatureHeights | PathAreaOp.FeatureStartPoint | PathAreaOp.FeatureBaseFaces | PathAreaOp.FeatureFinishDepth - # Tool Properties - obj.addProperty("App::PropertyLink", "ToolController", "Path", QtCore.QT_TRANSLATE_NOOP("App::Property", "The tool controller that will be used to calculate the path")) - - # Depth Properties - obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "The height needed to clear clamps and obstructions")) - obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "Rapid Safety Height between locations.")) - obj.addProperty("App::PropertyFloatConstraint", "StepDown", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "Incremental Step Down of Tool")) - obj.StepDown = (0.0, 0.01, 100.0, 0.5) - obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "Starting Depth of Tool- first cut depth in Z")) - obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "Final Depth of Tool- lowest value in Z")) - obj.addProperty("App::PropertyDistance", "FinishDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("App::Property", "Maximum material removed on final pass.")) + def initOperation(self, obj): # Face Properties obj.addProperty("App::PropertyEnumeration", "CutMode", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "The direction that the toolpath should go around the part ClockWise CW or CounterClockWise CCW")) @@ -90,182 +77,57 @@ class ObjectFace: obj.addProperty("App::PropertyEnumeration", "OffsetPattern", "Face", QtCore.QT_TRANSLATE_NOOP("App::Property", "clearing pattern to use")) obj.OffsetPattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle'] - # Start Point Properties - obj.addProperty("App::PropertyVector", "StartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "The start point of this path")) - obj.addProperty("App::PropertyBool", "UseStartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("App::Property", "make True, if specifying a Start Point")) - # Debug Parameters - obj.addProperty("App::PropertyString", "AreaParams", "Path") - obj.setEditorMode('AreaParams', 2) # hide - obj.addProperty("App::PropertyString", "PathParams", "Path") - obj.setEditorMode('PathParams', 2) # hide - obj.addProperty("Part::PropertyPartShape", "removalshape", "Path") - obj.setEditorMode('removalshape', 2) # hide - - if FreeCAD.GuiUp: - _ViewProviderFace(obj.ViewObject) - - obj.Proxy = self - - def onChanged(self, obj, prop): + def opOnChanged(self, obj, prop): PathLog.track(prop) - if prop == "StepOver": - if obj.StepOver == 0: - obj.StepOver = 1 - if prop in ['AreaParams', 'PathParams', 'removalshape']: - obj.setEditorMode(prop, 2) + if prop == "StepOver" and obj.StepOver == 0: + obj.StepOver = 1 - def __getstate__(self): - return None - - def __setstate__(self, state): - return None - - def setDepths(self, obj): - PathLog.track() - parentJob = PathUtils.findParentJob(obj) - if parentJob is None: - return - baseobject = parentJob.Base - if baseobject is None: - return - - d = PathUtils.guessDepths(baseobject.Shape, None) - obj.ClearanceHeight = d.clearance_height - obj.SafeHeight = d.safe_height + 1 - obj.StartDepth = d.safe_height - obj.FinalDepth = d.start_depth - obj.StepDown = obj.StartDepth.Value-obj.FinalDepth.Value - - def addFacebase(self, obj, ss, sub=""): - baselist = obj.Base - if baselist is None: - baselist = [] - if len(baselist) == 0: # When adding the first base object, guess at heights - subshape = [ss.Shape.getElement(sub)] - d = PathUtils.guessDepths(ss.Shape, subshape) + # default depths calculation not correct for facing + if prop == "Base" and len(obj.Base) == 1: + base, sub = obj.Base[0] + shape = base.Shape.getElement(sub[0]) + d = PathUtils.guessDepths(shape, None) obj.ClearanceHeight = d.clearance_height obj.SafeHeight = d.safe_height + 1 obj.StartDepth = d.safe_height - obj.FinalDepth = d.final_depth - obj.StepDown = obj.StartDepth.Value-obj.FinalDepth.Value + obj.FinalDepth = d.start_depth - item = (ss, sub) - if item in baselist: - FreeCAD.Console.PrintWarning(translate("Path", "this object already in the list" + "\n")) - elif PathUtils.findParentJob(obj).Base.Name != ss.Name: - PathLog.debug("parentbase: {}, selectionobj: {}".format(PathUtils.findParentJob(obj).Base.Name, ss.Name)) - FreeCAD.Console.PrintWarning(translate("Path", "Please select features from the Job model object" + "\n")) - else: - baselist.append(item) - PathLog.debug('baselist: {}'.format(baselist)) - obj.Base = baselist + def opUseProjection(self, obj): + return False - def getStock(self, obj): - """find and return a stock object from hosting project if any""" - for o in obj.InList: - if hasattr(o, "Group"): - for g in o.Group: - if hasattr(g, "Height_Allowance"): - return o - # not found? search one level up - for o in obj.InList: - return self.getStock(o) - return None - - @waiting_effects - def _buildPathArea(self, obj, baseobject): - """build the face path using PathArea""" - - PathLog.track() - boundary = Path.Area() - boundary.setPlane(makeWorkplane(baseobject)) - boundary.add(baseobject) - - stepover = (self.radius * 2) * (float(obj.StepOver)/100) - - pocketparams = {'Fill': 0, - 'Coplanar': 0, - 'PocketMode': 1, - 'SectionCount': -1, - 'Angle': obj.ZigZagAngle, - 'FromCenter': (obj.StartAt == "Center"), - 'PocketStepover': stepover, - 'PocketExtraOffset': 0 - obj.PassExtension.Value} + def opAreaParams(self, obj, isHole): + 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['PocketExtraOffset'] = 0 - obj.PassExtension.Value Pattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle'] - pocketparams['PocketMode'] = Pattern.index(obj.OffsetPattern) + 1 + params['PocketMode'] = Pattern.index(obj.OffsetPattern) + 1 - offsetval = self.radius - pocketparams['ToolRadius'] = offsetval + params['ToolRadius'] = self.radius - heights = [i for i in self.depthparams] - boundary.setParams(**pocketparams) - obj.AreaParams = str(boundary.getParams()) - sections = boundary.makeSections(mode=0, project=False, heights=heights) + return params - params = {'feedrate': self.horizFeed, - 'feedrate_v': self.vertFeed, - 'verbose': True, - 'resume_height': obj.StepDown, - 'retraction': obj.ClearanceHeight.Value} - - pp = [] + def opPathParams(self, obj, isHole): + params = {} + params['feedrate'] = self.horizFeed + params['feedrate_v'] = self.vertFeed + params['verbose'] = True + params['resume_height'] = obj.StepDown + params['retraction'] = obj.ClearanceHeight.Value if obj.UseStartPoint and obj.StartPoint is not None: params['start'] = obj.StartPoint + + return params - # store the params for debugging. Don't need the shape. - obj.PathParams = str(params) - PathLog.debug("Generating Path with params: {}".format(params)) - - for sec in sections: - shape = sec.getShape() - respath = Path.fromShapes(shape, **params) - # Insert any entry code to the layer - - # append the layer path - pp.extend(respath.Commands) - respath.Commands = pp - - return respath - - def execute(self, obj): - PathLog.track() - if not obj.Active: - path = Path.Path("(inactive operation)") - obj.Path = path - obj.ViewObject.Visibility = False - return - - commandlist = [] - toolLoad = obj.ToolController - - self.depthparams = depth_params( - clearance_height=obj.ClearanceHeight.Value, - safe_height=obj.SafeHeight.Value, - start_depth=obj.SafeHeight.Value, - step_down=obj.StepDown, - z_finish_step=obj.FinishDepth.Value, - final_depth=obj.FinalDepth.Value, - user_depths=None) - - if toolLoad is None or toolLoad.ToolNumber == 0: - FreeCAD.Console.PrintError("No Tool Controller is selected. We need a tool to build a Path.") - return - else: - self.vertFeed = toolLoad.VertFeed.Value - self.horizFeed = toolLoad.HorizFeed.Value - self.vertRapid = toolLoad.VertRapid.Value - self.horizRapid = toolLoad.HorizRapid.Value - tool = toolLoad.Proxy.getTool(toolLoad) - - if tool.Diameter == 0: - FreeCAD.Console.PrintError("No Tool found or diameter is zero. We need a tool to build a Path.") - return - else: - self.radius = tool.Diameter/2 - + def opShapes(self, obj, commandlist): commandlist.append(Path.Command("(" + obj.Label + ")")) # Facing is done either against base objects @@ -281,23 +143,19 @@ class ObjectFace: PathLog.debug('The base subobject is not a face') return planeshape = Part.makeCompound(faces) - PathLog.info("Working on a collection of faces {}".format(faces)) + PathLog.debug("Working on a collection of faces {}".format(faces)) # If no base object, do planing of top surface of entire model else: - parentJob = PathUtils.findParentJob(obj) - if parentJob is None: - PathLog.debug("No base object. No parent job found") - return - baseobject = parentJob.Base - if baseobject is None: - PathLog.debug("Parent job exists but no Base Object") + job = PathUtils.findParentJob(obj) + if not job or not job.Base: return + baseobject = job.Base planeshape = baseobject.Shape - PathLog.info("Working on a shape {}".format(baseobject.Name)) + PathLog.debug("Working on a shape {}".format(baseobject.Name)) # if user wants the boundbox, calculate that - PathLog.info("Boundary Shape: {}".format(obj.BoundaryShape)) + PathLog.debug("Boundary Shape: {}".format(obj.BoundaryShape)) bb = planeshape.BoundBox 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)) @@ -305,363 +163,22 @@ class ObjectFace: else: env = PathUtils.getEnvelope(partshape=planeshape, depthparams=self.depthparams) - # save the envelope for reference - obj.removalshape = env - - try: - commandlist.extend(self._buildPathArea(obj, env).Commands) - except Exception as e: - FreeCAD.Console.PrintError(e) - FreeCAD.Console.PrintError(translate("Path_MillFace", "The selected settings did not produce a valid path.\n")) - - # Let's finish by rapid to clearance...just for safety - commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value})) - - path = Path.Path(commandlist) - obj.Path = path - - -class _ViewProviderFace: - - def __init__(self, vobj): - vobj.Proxy = self - - def attach(self, vobj): - self.Object = vobj.Object - return - - def deleteObjectsOnReject(self): - return hasattr(self, 'deleteOnReject') and self.deleteOnReject - - def setEdit(self, vobj, mode=0): - FreeCADGui.Control.closeDialog() - taskd = TaskPanel(vobj.Object, self.deleteObjectsOnReject()) - taskd.obj = vobj.Object - taskd.setupUi() - FreeCADGui.Control.showDialog(taskd) - self.deleteOnReject = False - return True - - def getIcon(self): - return ":/icons/Path-Face.svg" - - def __getstate__(self): - return None - - def __setstate__(self, state): - return None - - -class CommandPathMillFace: - - def GetResources(self): - return {'Pixmap': 'Path-Face', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathFace", "Face"), - 'Accel': "P, O", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathFace", "Create a Facing Operation from a model or face")} - - def IsActive(self): - if FreeCAD.ActiveDocument is not None: - for o in FreeCAD.ActiveDocument.Objects: - if o.Name[:3] == "Job": - return True - return False - - def Activated(self): - # if everything is ok, execute and register the transaction in the undo/redo stack - FreeCAD.ActiveDocument.openTransaction(translate("PathFace", "Create Face")) - FreeCADGui.addModule("PathScripts.PathMillFace") - FreeCADGui.doCommand('obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Face")') - FreeCADGui.doCommand('PathScripts.PathMillFace.ObjectFace(obj)') - FreeCADGui.doCommand('obj.ViewObject.Proxy.deleteOnReject = True') - FreeCADGui.doCommand('PathScripts.PathUtils.addToJob(obj)') - FreeCADGui.doCommand('obj.ToolController = PathScripts.PathUtils.findToolController(obj)') - FreeCADGui.doCommand('PathScripts.PathMillFace.ObjectFace.setDepths(obj.Proxy, obj)') - - FreeCADGui.doCommand('obj.Active = True') - FreeCADGui.doCommand('obj.StepOver = 50') - FreeCADGui.doCommand('obj.StepDown = 1.0') - FreeCADGui.doCommand('obj.ZigZagAngle = 45.0') - FreeCAD.ActiveDocument.commitTransaction() - - FreeCADGui.doCommand('obj.ViewObject.startEditing()') - - -class _CommandSetFaceStartPoint: - def GetResources(self): - return {'Pixmap': 'Path-StartPoint', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathFace", "Pick Start Point"), - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathFace", "Pick Start Point")} - - def IsActive(self): - return FreeCAD.ActiveDocument is not None - - def setpoint(self, point, o): - obj = FreeCADGui.Selection.getSelection()[0] - obj.StartPoint.x = point.x - obj.StartPoint.y = point.y - - def Activated(self): - FreeCADGui.Snapper.getPoint(callback=self.setpoint) - - -class TaskPanel: - def __init__(self, obj, deleteOnReject): - FreeCAD.ActiveDocument.openTransaction(translate("Path_MillFace", "Mill Facing Operation")) - # self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/MillFaceEdit.ui") - self.form = FreeCADGui.PySideUic.loadUi(":/panels/MillFaceEdit.ui") - self.deleteOnReject = deleteOnReject - self.obj = obj - self.isDirty = True - - def accept(self): - FreeCADGui.Control.closeDialog() - FreeCADGui.ActiveDocument.resetEdit() - FreeCAD.ActiveDocument.commitTransaction() - FreeCADGui.Selection.removeObserver(self.s) - if self.isDirty: - FreeCAD.ActiveDocument.recompute() - - def reject(self): - FreeCADGui.Control.closeDialog() - FreeCADGui.ActiveDocument.resetEdit() - FreeCAD.ActiveDocument.abortTransaction() - FreeCADGui.Selection.removeObserver(self.s) - if self.deleteOnReject: - FreeCAD.ActiveDocument.openTransaction(translate("Path_MillFace", "Uncreate Mill Face Operation")) - FreeCAD.ActiveDocument.removeObject(self.obj.Name) - FreeCAD.ActiveDocument.commitTransaction() - FreeCAD.ActiveDocument.recompute() - - def clicked(self, button): - if button == QtGui.QDialogButtonBox.Apply: - self.getFields() - FreeCAD.ActiveDocument.recompute() - self.isDirty = False - - def getFields(self): - if self.obj: - if hasattr(self.obj, "StartDepth"): - self.obj.StartDepth = FreeCAD.Units.Quantity(self.form.startDepth.text()).Value - if hasattr(self.obj, "FinalDepth"): - self.obj.FinalDepth = FreeCAD.Units.Quantity(self.form.finalDepth.text()).Value - if hasattr(self.obj, "FinishDepth"): - self.obj.FinishDepth = FreeCAD.Units.Quantity(self.form.finishDepth.text()).Value - if hasattr(self.obj, "StepDown"): - self.obj.StepDown = FreeCAD.Units.Quantity(self.form.stepDown.text()).Value - if hasattr(self.obj, "SafeHeight"): - self.obj.SafeHeight = FreeCAD.Units.Quantity(self.form.safeHeight.text()).Value - if hasattr(self.obj, "ClearanceHeight"): - self.obj.ClearanceHeight = FreeCAD.Units.Quantity(self.form.clearanceHeight.text()).Value - if hasattr(self.obj, "PassExtension"): - self.obj.PassExtension = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value - if hasattr(self.obj, "CutMode"): - self.obj.CutMode = str(self.form.cutMode.currentText()) - if hasattr(self.obj, "ZigZagAngle"): - self.obj.ZigZagAngle = FreeCAD.Units.Quantity(self.form.zigZagAngle.text()).Value - if hasattr(self.obj, "StepOver"): - self.obj.StepOver = self.form.stepOverPercent.value() - if hasattr(self.obj, "BoundaryShape"): - self.obj.BoundaryShape = str(self.form.boundaryShape.currentText()) - if hasattr(self.obj, "OffsetPattern"): - self.obj.OffsetPattern = str(self.form.offsetpattern.currentText()) - - if hasattr(self.obj, "ToolController"): - tc = PathUtils.findToolController(self.obj, self.form.uiToolController.currentText()) - self.obj.ToolController = tc - - self.isDirty = True - - def setFields(self): - self.form.startDepth.setText(FreeCAD.Units.Quantity(self.obj.StartDepth.Value, FreeCAD.Units.Length).UserString) - self.form.finalDepth.setText(FreeCAD.Units.Quantity(self.obj.FinalDepth.Value, FreeCAD.Units.Length).UserString) - self.form.finishDepth.setText(FreeCAD.Units.Quantity(self.obj.FinishDepth.Value, FreeCAD.Units.Length).UserString) - self.form.stepDown.setText(FreeCAD.Units.Quantity(self.obj.StepDown, FreeCAD.Units.Length).UserString) - self.form.safeHeight.setText(FreeCAD.Units.Quantity(self.obj.SafeHeight.Value, FreeCAD.Units.Length).UserString) - self.form.clearanceHeight.setText(FreeCAD.Units.Quantity(self.obj.ClearanceHeight.Value, FreeCAD.Units.Length).UserString) - - self.form.stepOverPercent.setValue(self.obj.StepOver) - self.form.zigZagAngle.setText(FreeCAD.Units.Quantity(self.obj.ZigZagAngle, FreeCAD.Units.Angle).UserString) - self.form.extraOffset.setValue(self.obj.PassExtension.Value) - - index = self.form.cutMode.findText( - self.obj.CutMode, QtCore.Qt.MatchFixedString) - if index >= 0: - - self.form.cutMode.blockSignals(True) - self.form.cutMode.setCurrentIndex(index) - self.form.cutMode.blockSignals(False) - - index = self.form.boundaryShape.findText( - self.obj.BoundaryShape, QtCore.Qt.MatchFixedString) - if index >= 0: - self.form.boundaryShape.blockSignals(True) - self.form.boundaryShape.setCurrentIndex(index) - self.form.boundaryShape.blockSignals(False) - - index = self.form.offsetpattern.findText( - self.obj.OffsetPattern, QtCore.Qt.MatchFixedString) - if index >= 0: - self.form.offsetpattern.blockSignals(True) - self.form.offsetpattern.setCurrentIndex(index) - self.form.offsetpattern.blockSignals(False) - - for i in self.obj.Base: - for sub in i[1]: - self.form.baseList.addItem(i[0].Name + "." + sub) - - controllers = PathUtils.getToolControllers(self.obj) - labels = [c.Label for c in controllers] - self.form.uiToolController.blockSignals(True) - self.form.uiToolController.addItems(labels) - self.form.uiToolController.blockSignals(False) - - if self.obj.ToolController is None: - self.obj.ToolController = PathUtils.findToolController(self.obj) - - if self.obj.ToolController is not None: - index = self.form.uiToolController.findText( - self.obj.ToolController.Label, QtCore.Qt.MatchFixedString) - if index >= 0: - self.form.uiToolController.blockSignals(True) - self.form.uiToolController.setCurrentIndex(index) - self.form.uiToolController.blockSignals(False) - - def open(self): - self.s = SelObserver() - # install the function mode resident - FreeCADGui.Selection.addObserver(self.s) - - def addBase(self): - # check that the selection contains exactly what we want - selection = FreeCADGui.Selection.getSelectionEx() - - if len(selection) != 1: - FreeCAD.Console.PrintError(translate("PathProject", "Please select only faces from one solid\n")) - return - sel = selection[0] - if not sel.HasSubObjects: - FreeCAD.Console.PrintError(translate("PathProject", "Please select faces from one solid\n")) - return - if not selection[0].SubObjects[0].ShapeType == "Face": - FreeCAD.Console.PrintError(translate("PathProject", "Please select faces from one solid\n")) - return - for i in sel.SubElementNames: - self.obj.Proxy.addFacebase(self.obj, sel.Object, i) - - self.setFields() # defaults may have changed. Reload. - self.form.baseList.clear() - - for i in self.obj.Base: - for sub in i[1]: - self.form.baseList.addItem(i[0].Name + "." + sub) - - def deleteBase(self): - dlist = self.form.baseList.selectedItems() - newlist = [] - for d in dlist: - deletebase = d.text().partition(".")[0] - deletesub = d.text().partition(".")[2] - - for i in self.obj.Base: - sublist = [] - basesubs = i[1] - for sub in basesubs: - if sub != deletesub: - sublist.append(sub) - if len(sublist) >= 1: - newlist.append((deletebase, tuple(sublist))) - - if i[0].Name != d.text().partition(".")[0] and d.text().partition(".")[2] not in i[1]: - newlist.append(i) - self.form.baseList.takeItem(self.form.baseList.row(d)) - self.obj.Base = newlist - - def itemActivated(self): - FreeCADGui.Selection.clearSelection() - slist = self.form.baseList.selectedItems() - for i in slist: - objstring = i.text().partition(".") - obj = FreeCAD.ActiveDocument.getObject(objstring[0]) - if objstring[2] != "": - FreeCADGui.Selection.addSelection(obj, objstring[2]) - else: - FreeCADGui.Selection.addSelection(obj) - - FreeCADGui.updateGui() - - def reorderBase(self): - newlist = [] - for i in range(self.form.baseList.count()): - s = self.form.baseList.item(i).text() - objstring = s.partition(".") - - obj = FreeCAD.ActiveDocument.getObject(objstring[0]) - item = (obj, str(objstring[2])) - newlist.append(item) - self.obj.Base = newlist - - def getStandardButtons(self): - return int(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Cancel) - - def resetObject(self, remove=None): - "transfers the values from the widget to the object" - - self.obj.touch() - FreeCAD.ActiveDocument.recompute() - - def setupUi(self): - - # Connect Signals and Slots - # Base Controls - self.form.baseList.itemSelectionChanged.connect(self.itemActivated) - self.form.addBase.clicked.connect(self.addBase) - self.form.deleteBase.clicked.connect(self.deleteBase) - self.form.reorderBase.clicked.connect(self.reorderBase) - - # Depths - self.form.startDepth.editingFinished.connect(self.getFields) - self.form.finalDepth.editingFinished.connect(self.getFields) - self.form.finishDepth.editingFinished.connect(self.getFields) - self.form.stepDown.editingFinished.connect(self.getFields) - - # Heights - self.form.safeHeight.editingFinished.connect(self.getFields) - self.form.clearanceHeight.editingFinished.connect(self.getFields) - - # operation - self.form.cutMode.currentIndexChanged.connect(self.getFields) - self.form.extraOffset.editingFinished.connect(self.getFields) - self.form.boundaryShape.currentIndexChanged.connect(self.getFields) - self.form.stepOverPercent.editingFinished.connect(self.getFields) - self.form.offsetpattern.currentIndexChanged.connect(self.getFields) - self.form.zigZagAngle.editingFinished.connect(self.getFields) - self.form.uiToolController.currentIndexChanged.connect(self.getFields) - - self.setFields() - - sel = FreeCADGui.Selection.getSelectionEx() - if len(sel) != 0 and sel[0].HasSubObjects: - self.addBase() - - -class SelObserver: - def __init__(self): - import PathScripts.PathSelection as PST - PST.pocketselect() - - def __del__(self): - import PathScripts.PathSelection as PST - PST.clear() - - def addSelection(self, doc, obj, sub, pnt): - FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj + ')') - FreeCADGui.updateGui() - -if FreeCAD.GuiUp: - # register the FreeCAD command - FreeCADGui.addCommand('Path_MillFace', CommandPathMillFace()) - FreeCADGui.addCommand('Set_FaceStartPoint', _CommandSetFaceStartPoint()) - - -FreeCAD.Console.PrintLog("Loading PathMillFace... done\n") + return [(env, False)] + + def opSetDefaultValues(self, obj): + obj.StepOver = 50 + obj.ZigZagAngle = 45.0 + + # need to overwrite the default depth calculations for facing + job = PathUtils.findParentJob(obj) + if job and job.Base: + d = PathUtils.guessDepths(job.Base.Shape, None) + obj.ClearanceHeight = d.clearance_height + obj.SafeHeight = d.safe_height + 1 + obj.StartDepth = d.safe_height + obj.FinalDepth = d.start_depth + +def Create(name): + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) + proxy = ObjectFace(obj) + return obj diff --git a/src/Mod/Path/PathScripts/PathMillFaceGui.py b/src/Mod/Path/PathScripts/PathMillFaceGui.py new file mode 100644 index 0000000000..21fa10d157 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathMillFaceGui.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- + +# *************************************************************************** +# * * +# * Copyright (c) 2017 sliptonic * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** + +import FreeCAD +import FreeCADGui +import Path +import PathScripts.PathAreaOpGui as PathAreaOpGui +import PathScripts.PathMillFace as PathMillFace +import PathScripts.PathPocketBaseGui as PathPocketBaseGui + +from PySide import QtCore + +class TaskPanelOpPage(PathPocketBaseGui.TaskPanelOpPage): + + def pocketFeatures(self): + return PathPocketBaseGui.FeatureFacing + +PathAreaOpGui.SetupOperation('MillFace', + PathMillFace.Create, + TaskPanelOpPage, + 'Path-Face', + QtCore.QT_TRANSLATE_NOOP("PathFace", "Face"), + "P, O", + QtCore.QT_TRANSLATE_NOOP("PathFace", "Create a Facing Operation from a model or face")) + +FreeCAD.Console.PrintLog("Loading PathMillFaceGui... done\n") + diff --git a/src/Mod/Path/PathScripts/PathPocket.py b/src/Mod/Path/PathScripts/PathPocket.py index 5d0dbbb9e8..8813c92cd1 100644 --- a/src/Mod/Path/PathScripts/PathPocket.py +++ b/src/Mod/Path/PathScripts/PathPocket.py @@ -46,6 +46,9 @@ def translate(context, text, disambig=None): class ObjectPocket(PathAreaOp.ObjectOp): + def opFeatures(self, obj): + return PathAreaOp.FeatureTool | PathAreaOp.FeatureDepths | PathAreaOp.FeatureHeights | PathAreaOp.FeatureStartPoint | PathAreaOp.FeatureBaseFaces | PathAreaOp.FeatureFinishDepth + def initOperation(self, obj): PathLog.track() @@ -61,9 +64,6 @@ class ObjectPocket(PathAreaOp.ObjectOp): obj.OffsetPattern = ['ZigZag', 'Offset', 'Spiral', 'ZigZagOffset', 'Line', 'Grid', 'Triangle'] obj.addProperty("App::PropertyBool", "MinTravel", "Pocket", QtCore.QT_TRANSLATE_NOOP("App::Property", "Use 3D Sorting of Path")) - def opFeatures(self, obj): - return PathAreaOp.FeatureTool | PathAreaOp.FeatureDepths | PathAreaOp.FeatureHeights | PathAreaOp.FeatureStartPoint | PathAreaOp.FeatureBaseFaces | PathAreaOp.FeatureFinishDepth - def opUseProjection(self, obj): return False diff --git a/src/Mod/Path/PathScripts/PathPocketBaseGui.py b/src/Mod/Path/PathScripts/PathPocketBaseGui.py new file mode 100644 index 0000000000..da1c009488 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathPocketBaseGui.py @@ -0,0 +1,105 @@ +# -*- coding: utf-8 -*- + +# *************************************************************************** +# * * +# * Copyright (c) 2017 sliptonic * +# * * +# * This program is free software; you can redistribute it and/or modify * +# * it under the terms of the GNU Lesser General Public License (LGPL) * +# * as published by the Free Software Foundation; either version 2 of * +# * the License, or (at your option) any later version. * +# * for detail see the LICENCE text file. * +# * * +# * This program is distributed in the hope that it will be useful, * +# * but WITHOUT ANY WARRANTY; without even the implied warranty of * +# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +# * GNU Library General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with this program; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# *************************************************************************** + +import FreeCAD +import FreeCADGui +import Path +import PathScripts.PathAreaOpGui as PathAreaOpGui +import PathScripts.PathLog as PathLog +import PathScripts.PathPocket as PathPocket +import PathScripts.PathSelection as PathSelection + +from PathScripts import PathUtils +from PySide import QtCore, QtGui + +def translate(context, text, disambig=None): + return QtCore.QCoreApplication.translate(context, text, disambig) + +FeaturePocket = 0x01 +FeatureFacing = 0x02 + +class TaskPanelOpPage(PathAreaOpGui.TaskPanelPage): + + def getForm(self): + form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpPocketFullEdit.ui") + + if not FeaturePocket & self.pocketFeatures(): + form.pocketWidget.hide() + + if not FeatureFacing & self.pocketFeatures(): + form.facingWidget.hide() + + return form + + def getFields(self, obj): + self.obj.CutMode = str(self.form.cutMode.currentText()) + self.obj.StepOver = self.form.stepOverPercent.value() + self.obj.OffsetPattern = str(self.form.offsetPattern.currentText()) + self.obj.ZigZagAngle = FreeCAD.Units.Quantity(self.form.zigZagAngle.text()).Value + + tc = PathUtils.findToolController(self.obj, self.form.toolController.currentText()) + self.obj.ToolController = tc + + if FeaturePocket & self.pocketFeatures(): + self.obj.MaterialAllowance = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value + self.obj.UseStartPoint = self.form.useStartPoint.isChecked() + + if FeatureFacing & self.pocketFeatures(): + self.obj.PassExtension = FreeCAD.Units.Quantity(self.form.passExtension.text()).Value + self.obj.BoundaryShape = str(self.form.boundaryShape.currentText()) + + def setFields(self, obj): + self.form.zigZagAngle.setText(FreeCAD.Units.Quantity(self.obj.ZigZagAngle, FreeCAD.Units.Angle).UserString) + self.form.stepOverPercent.setValue(self.obj.StepOver) + + self.selectInComboBox(self.obj.OffsetPattern, self.form.offsetPattern) + self.selectInComboBox(self.obj.CutMode, self.form.cutMode) + self.setupToolController(self.obj, self.form.toolController) + + if FeaturePocket & self.pocketFeatures(): + self.form.useStartPoint.setChecked(self.obj.UseStartPoint) + self.form.extraOffset.setText(FreeCAD.Units.Quantity(self.obj.MaterialAllowance.Value, FreeCAD.Units.Length).UserString) + + if FeatureFacing & self.pocketFeatures(): + self.form.passExtension.setText(FreeCAD.Units.Quantity(self.obj.PassExtension.Value, FreeCAD.Units.Length).UserString) + self.selectInComboBox(self.obj.BoundaryShape, self.form.boundaryShape) + + def getSignalsForUpdate(self, obj): + signals = [] + + signals.append(self.form.cutMode.currentIndexChanged) + signals.append(self.form.offsetPattern.currentIndexChanged) + signals.append(self.form.stepOverPercent.editingFinished) + signals.append(self.form.zigZagAngle.editingFinished) + signals.append(self.form.toolController.currentIndexChanged) + + if FeaturePocket & self.pocketFeatures(): + signals.append(self.form.extraOffset.editingFinished) + signals.append(self.form.useStartPoint.clicked) + + if FeatureFacing & self.pocketFeatures(): + signals.append(self.form.boundaryShape.currentIndexChanged) + signals.append(self.form.passExtension.editingFinished) + + return signals diff --git a/src/Mod/Path/PathScripts/PathPocketGui.py b/src/Mod/Path/PathScripts/PathPocketGui.py index 4b28871470..4e46d9875b 100644 --- a/src/Mod/Path/PathScripts/PathPocketGui.py +++ b/src/Mod/Path/PathScripts/PathPocketGui.py @@ -26,55 +26,15 @@ import FreeCAD import FreeCADGui import Path import PathScripts.PathAreaOpGui as PathAreaOpGui -import PathScripts.PathLog as PathLog import PathScripts.PathPocket as PathPocket -import PathScripts.PathSelection as PathSelection +import PathScripts.PathPocketBaseGui as PathPocketBaseGui -from PathScripts import PathUtils -from PySide import QtCore, QtGui +from PySide import QtCore -def translate(context, text, disambig=None): - return QtCore.QCoreApplication.translate(context, text, disambig) +class TaskPanelOpPage(PathPocketBaseGui.TaskPanelOpPage): -class TaskPanelOpPage(PathAreaOpGui.TaskPanelPage): - - def getForm(self): - return FreeCADGui.PySideUic.loadUi(":/panels/PageOpPocketEdit.ui") - - def getFields(self, obj): - self.obj.MaterialAllowance = FreeCAD.Units.Quantity(self.form.extraOffset.text()).Value - self.obj.CutMode = str(self.form.cutMode.currentText()) - self.obj.OffsetPattern = str(self.form.offsetPattern.currentText()) - self.obj.ZigZagAngle = FreeCAD.Units.Quantity(self.form.zigZagAngle.text()).Value - self.obj.StepOver = self.form.stepOverPercent.value() - self.obj.UseStartPoint = self.form.useStartPoint.isChecked() - - tc = PathUtils.findToolController(self.obj, self.form.toolController.currentText()) - self.obj.ToolController = tc - - def setFields(self, obj): - self.form.extraOffset.setText(FreeCAD.Units.Quantity(self.obj.MaterialAllowance.Value, FreeCAD.Units.Length).UserString) - self.form.useStartPoint.setChecked(self.obj.UseStartPoint) - self.form.zigZagAngle.setText(FreeCAD.Units.Quantity(self.obj.ZigZagAngle, FreeCAD.Units.Angle).UserString) - self.form.stepOverPercent.setValue(self.obj.StepOver) - - self.selectInComboBox(self.obj.OffsetPattern, self.form.offsetPattern) - self.selectInComboBox(self.obj.CutMode, self.form.cutMode) - self.setupToolController(self.obj, self.form.toolController) - - def getSignalsForUpdate(self, obj): - signals = [] - # operation - signals.append(self.form.cutMode.currentIndexChanged) - signals.append(self.form.useStartPoint.clicked) - - # Pattern - signals.append(self.form.offsetPattern.currentIndexChanged) - signals.append(self.form.stepOverPercent.editingFinished) - signals.append(self.form.zigZagAngle.editingFinished) - signals.append(self.form.extraOffset.editingFinished) - signals.append(self.form.toolController.currentIndexChanged) - return signals + def pocketFeatures(self): + return PathPocketBaseGui.FeaturePocket PathAreaOpGui.SetupOperation('Pocket', PathPocket.Create, diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py index b42b8f01b9..753102d735 100644 --- a/src/Mod/Path/PathScripts/PathSelection.py +++ b/src/Mod/Path/PathScripts/PathSelection.py @@ -163,6 +163,7 @@ def surfaceselect(): def select(op): opsel = {} opsel['Contour'] = contourselect + opsel['MillFace'] = pocketselect opsel['Pocket'] = pocketselect opsel['Profile Edges'] = eselect opsel['Profile Faces'] = profileselect