Converted MillFace to be based on PathAreaop.

This commit is contained in:
Markus Lampert
2017-08-06 15:48:08 -07:00
committed by Yorik van Havre
parent 6b8960d125
commit 4e8d0cbea3
11 changed files with 300 additions and 608 deletions

View File

@@ -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

View File

@@ -64,7 +64,7 @@
<file>panels/PageBaseGeometryEdit.ui</file>
<file>panels/PageDepthsEdit.ui</file>
<file>panels/PageHeightsEdit.ui</file>
<file>panels/PageOpPocketEdit.ui</file>
<file>panels/PageOpPocketFullEdit.ui</file>
<file>panels/PageOpProfileFullEdit.ui</file>
<file>panels/PocketEdit.ui</file>
<file>panels/PointEdit.ui</file>

View File

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>398</width>
<height>330</height>
<height>454</height>
</rect>
</property>
<property name="windowTitle">
@@ -36,9 +36,49 @@
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="facingWidget" native="true">
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>Boundary Shape</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="boundaryShape">
<item>
<property name="text">
<string>Perimeter</string>
</property>
</item>
<item>
<property name="text">
<string>Boundbox</string>
</property>
</item>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Pass Extension</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::InputField" name="passExtension"/>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget" native="true">
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
@@ -139,17 +179,23 @@
</property>
</widget>
</item>
<item row="4" column="0">
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="pocketWidget" native="true">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_7">
<property name="text">
<string>Material Allowance</string>
</property>
</widget>
</item>
<item row="4" column="1">
<item row="0" column="1">
<widget class="Gui::InputField" name="extraOffset"/>
</item>
<item row="5" column="0">
<item row="1" column="0">
<widget class="QCheckBox" name="useStartPoint">
<property name="text">
<string>Use Start Point</string>
@@ -159,6 +205,19 @@
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>

View File

@@ -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

View File

@@ -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())

View File

@@ -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

View File

@@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2017 sliptonic <shopinthewoods@gmail.com> *
# * *
# * 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")

View File

@@ -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

View File

@@ -0,0 +1,105 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2017 sliptonic <shopinthewoods@gmail.com> *
# * *
# * 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

View File

@@ -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,

View File

@@ -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