diff --git a/src/Mod/Path/Gui/Resources/panels/DlgJobTemplateExport.ui b/src/Mod/Path/Gui/Resources/panels/DlgJobTemplateExport.ui index d7bb72fa23..6f9b1d0fd9 100644 --- a/src/Mod/Path/Gui/Resources/panels/DlgJobTemplateExport.ui +++ b/src/Mod/Path/Gui/Resources/panels/DlgJobTemplateExport.ui @@ -7,7 +7,7 @@ 0 0 422 - 432 + 434 @@ -110,7 +110,7 @@ - + <html><head/><body><p>Enable to include the default values in the template.</p></body></html> @@ -121,26 +121,36 @@ true - - + + - <html><head/><body><p>Enable to include the default rapid tool speeds in the template.</p></body></html> + <html><head/><body><p>Enable to include the default heights for operations in the template.</p></body></html> - Tool Rapid Speeds + Operation Heights true - - + + + + Operation Depths + + + true + + + + + - <html><head/><body><p>Enable to include the default heights for operations in the template.</p></body></html> + <html><head/><body><p>Enable to include the default rapid tool speeds in the template.</p></body></html> - Operation Heights + Tool Rapid Speeds true @@ -189,9 +199,10 @@ stockGroup stockExtent stockPlacement - defaultsGroup - defaultToolRapid - defaultOperationHeights + settingsGroup + settingOperationHeights + settingOperationDepths + settingToolRapid toolsGroup toolsList buttonBox diff --git a/src/Mod/Path/PathScripts/PathAreaOp.py b/src/Mod/Path/PathScripts/PathAreaOp.py index 3614fe5e06..d893a7fcca 100644 --- a/src/Mod/Path/PathScripts/PathAreaOp.py +++ b/src/Mod/Path/PathScripts/PathAreaOp.py @@ -140,29 +140,11 @@ class ObjectOp(PathOp.ObjectOp): if shape: bb = shape.BoundBox - obj.StartDepth = bb.ZMax - obj.FinalDepth = bb.ZMin - if PathOp.FeatureStepDown & self.opFeatures(obj): - obj.StepDown = 1.0 + obj.OpStartDepth = bb.ZMax + obj.OpFinalDepth = bb.ZMin else: - obj.StartDepth = 1.0 - obj.FinalDepth = 0.0 - if PathOp.FeatureStepDown & self.opFeatures(obj): - obj.StepDown = 1.0 - - if PathOp.FeatureHeights & self.opFeatures(obj): - try: - shape = self.areaOpShapeForDepths(obj) - except: - shape = None - - if shape: - bb = shape.BoundBox - obj.ClearanceHeight = bb.ZMax + 5.0 - obj.SafeHeight = bb.ZMax + 3.0 - else: - obj.ClearanceHeight = 10.0 - obj.SafeHeight = 8.0 + obj.OpStartDepth = 1.0 + obj.OpFinalDepth = 0.0 self.areaOpSetDefaultValues(obj) diff --git a/src/Mod/Path/PathScripts/PathEngrave.py b/src/Mod/Path/PathScripts/PathEngrave.py index 2e8e227318..730a3c9195 100644 --- a/src/Mod/Path/PathScripts/PathEngrave.py +++ b/src/Mod/Path/PathScripts/PathEngrave.py @@ -184,15 +184,13 @@ class ObjectEngrave(PathOp.ObjectOp): job = PathUtils.findParentJob(obj) if job and job.Base: bb = job.Base.Shape.BoundBox - obj.StartDepth = bb.ZMax - obj.FinalDepth = bb.ZMin - obj.ClearanceHeight = bb.ZMax + 5.0 - obj.SafeHeight = bb.ZMax + 3.0 + obj.OpStartDepth = bb.ZMax + obj.OpFinalDepth = bb.ZMin else: - obj.FinalDepth = -0.1 - if obj.StartDepth.Value != obj.FinalDepth.Value: + obj.OpFinalDepth = -0.1 + if obj.OpStartDepth.Value != obj.OpFinalDepth.Value: # maintain behaviour of a single run - obj.StepDown = obj.StartDepth.Value - obj.FinalDepth.Value + obj.OpStepDown = obj.OpStartDepth.Value - obj.OpFinalDepth.Value def Create(name): '''Create(name) ... Creates and returns a Engrave operation.''' diff --git a/src/Mod/Path/PathScripts/PathJobCmd.py b/src/Mod/Path/PathScripts/PathJobCmd.py index 151b64f928..82289fb03a 100644 --- a/src/Mod/Path/PathScripts/PathJobCmd.py +++ b/src/Mod/Path/PathScripts/PathJobCmd.py @@ -158,6 +158,15 @@ class DlgJobTemplateExport: spHint = "%s" % job.Stock.Placement self.dialog.stockPlacementHint.setText(spHint) + rapidChanged = not job.SetupSheet.Proxy.hasDefaultToolRapids() + depthsChanged = not job.SetupSheet.Proxy.hasDefaultOperationDepths() + heightsChanged = not job.SetupSheet.Proxy.hasDefaultOperationHeights() + settingsChanged = rapidChanged or depthsChanged or heightsChanged + self.dialog.settingsGroup.setChecked(settingsChanged) + self.dialog.settingToolRapid.setChecked(rapidChanged) + self.dialog.settingOperationDepths.setChecked(depthsChanged) + self.dialog.settingOperationHeights.setChecked(heightsChanged) + for tc in sorted(job.ToolController, key=lambda o: o.Label): item = QtGui.QListWidgetItem(tc.Label) item.setData(self.DataObject, tc) @@ -189,12 +198,14 @@ class DlgJobTemplateExport: def includeStockPlacement(self): return self.dialog.stockPlacement.isChecked() - def includeDefaults(self): - return self.dialog.defaultsGroup.isChecked() - def includeDefaultToolRapid(self): - return self.dialog.defaultToolRapid.isChecked() - def includeDefaultOperationHeights(self): - return self.dialog.defaultOperationHeights.isChecked() + def includeSettings(self): + return self.dialog.settingsGroup.isChecked() + def includeSettingToolRapid(self): + return self.dialog.settingToolRapid.isChecked() + def includeSettingOperationHeights(self): + return self.dialog.settingOperationHeights.isChecked() + def includeSettingOperationDepths(self): + return self.dialog.settingOperationDepths.isChecked() def exec_(self): return self.dialog.exec_() @@ -255,9 +266,9 @@ class CommandJobTemplateExport: # setup sheet setupSheetAttrs = None if dialog: - setupSheetAttrs = job.Proxy.setupSheet.templateAttributes(dialog.includeDefaultToolRapid(), dialog.includeDefaultOperationHeights()) + setupSheetAttrs = job.Proxy.setupSheet.templateAttributes(dialog.includeSettingToolRapid(), dialog.includeSettingOperationHeights(), dialog.includeSettingOperationDepths()) else: - setupSheetAttrs = job.Proxy.setupSheet.templateAttributes(True, True) + setupSheetAttrs = job.Proxy.setupSheet.templateAttributes(True, True, True) if setupSheetAttrs: attrs[PathJob.JobTemplate.SetupSheet] = setupSheetAttrs diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py index 22794fb587..c86e011e9a 100644 --- a/src/Mod/Path/PathScripts/PathMillFace.py +++ b/src/Mod/Path/PathScripts/PathMillFace.py @@ -71,10 +71,8 @@ class ObjectFace(PathPocketBase.ObjectPocket): 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.start_depth + obj.OpStartDepth = d.safe_height + obj.OpFinalDepth = d.start_depth def areaOpShapes(self, obj): '''areaOpShapes(obj) ... return top face''' @@ -118,10 +116,8 @@ class ObjectFace(PathPocketBase.ObjectPocket): 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 + obj.OpStartDepth = d.safe_height + obj.OpFinalDepth = d.start_depth def Create(name): '''Create(name) ... Creates and returns a Mill Facing operation.''' diff --git a/src/Mod/Path/PathScripts/PathOp.py b/src/Mod/Path/PathScripts/PathOp.py index c00c8b15c7..afca092286 100644 --- a/src/Mod/Path/PathScripts/PathOp.py +++ b/src/Mod/Path/PathScripts/PathOp.py @@ -246,13 +246,13 @@ class ObjectOp(object): obj.OpToolDiameter = 1.0 if FeatureDepths & features: - obj.setExpression('StartDepth', 'OpStartDepth') - obj.setExpression('FinalDepth', 'OpFinalDepth') + obj.setExpression('StartDepth', job.SetupSheet.StartDepthExpression) + obj.setExpression('FinalDepth', job.SetupSheet.FinalDepthExpression) obj.OpStartDepth = 1.0 obj.OpFinalDepth = 0.0 if FeatureStepDown & features: - obj.setExpression('StepDown', 'OpToolDiameter') + obj.setExpression('StepDown', job.SetupSheet.StepDownExpression) if FeatureHeights & features: if job.SetupSheet.SafeHeightExpression: @@ -264,6 +264,7 @@ class ObjectOp(object): obj.UseStartPoint = False self.opSetDefaultValues(obj) + obj.recompute() def _setBaseAndStock(self, obj, ignoreErrors=False): job = PathUtils.findParentJob(obj) diff --git a/src/Mod/Path/PathScripts/PathPocketShape.py b/src/Mod/Path/PathScripts/PathPocketShape.py index 1c9a90b0b2..0a9a6f2406 100644 --- a/src/Mod/Path/PathScripts/PathPocketShape.py +++ b/src/Mod/Path/PathScripts/PathPocketShape.py @@ -142,8 +142,8 @@ class ObjectPocket(PathPocketBase.ObjectPocket): job = PathUtils.findParentJob(obj) if job and job.Stock: bb = job.Stock.Shape.BoundBox - obj.FinalDepth = bb.ZMin - obj.StartDepth = bb.ZMax + obj.OpFinalDepth = bb.ZMin + obj.OpStartDepth = bb.ZMax def Create(name): '''Create(name) ... Creates and returns a Pocket operation.''' diff --git a/src/Mod/Path/PathScripts/PathSetupSheet.py b/src/Mod/Path/PathScripts/PathSetupSheet.py index b0a434eefd..b4951ec866 100644 --- a/src/Mod/Path/PathScripts/PathSetupSheet.py +++ b/src/Mod/Path/PathScripts/PathSetupSheet.py @@ -27,6 +27,9 @@ import Path import PathScripts.PathLog as PathLog import PySide +from PathScripts.PathGeom import PathGeom + + __title__ = "Setup Sheet for a Job." __author__ = "sliptonic (Brad Collette)" __url__ = "http://www.freecadweb.org" @@ -44,10 +47,16 @@ def translate(context, text, disambig=None): class Template: HorizRapid = 'HorizRapid' VertRapid = 'VertRapid' - SafeHeightOffset = 'SafeHeight' + SafeHeightOffset = 'SafeHeightOffset' SafeHeightExpression = 'SafeHeightExpression' - ClearanceHeightOffset = 'ClearanceHeight' + ClearanceHeightOffset = 'ClearanceHeightOffset' ClearanceHeightExpression = 'ClearanceHeightExpression' + StartDepthExpression = 'StartDepthExpression' + FinalDepthExpression = 'FinalDepthExpression' + StepDownExpression = 'StepDownExpression' + + All = [HorizRapid, VertRapid, SafeHeightOffset, SafeHeightExpression, ClearanceHeightOffset, ClearanceHeightExpression, StartDepthExpression, FinalDepthExpression, StepDownExpression] + def _traverseTemplateAttributes(attrs, codec): coded = {} @@ -73,6 +82,15 @@ It's mostly a convencience wrapper around Spreadsheet. TemplateReference = '${SetupSheet}' + DefaultSafeHeightOffset = '3 mm' + DefaultClearanceHeightOffset = '5 mm' + DefaultSafeHeightExpression = "StartDepth+${SetupSheet}.SafeHeightOffset" + DefaultClearanceHeightExpression = "StartDepth+${SetupSheet}.ClearanceHeightOffset" + + DefaultStartDepthExpression = 'OpStartDepth' + DefaultFinalDepthExpression = 'OpFinalDepth' + DefaultStepDownExpression = 'OpToolDiameter' + def __init__(self, obj): self.obj = obj obj.addProperty('App::PropertySpeed', 'VertRapid', 'ToolController', translate('PathSetupSheet', 'Default speed for horizontal rapid moves.')) @@ -83,10 +101,18 @@ It's mostly a convencience wrapper around Spreadsheet. obj.addProperty('App::PropertyLength', 'ClearanceHeightOffset', 'OperationHeights', translate('PathSetupSheet', 'The usage of this field depends on ClearanceHeightExpression - by default is value is added to StartDepth and used for ClearanceHeight of an operation.')) obj.addProperty('App::PropertyString', 'ClearanceHeightExpression', 'OperationHeights', translate('PathSetupSheet', 'Expression set for the ClearanceHeight of new operations.')) - obj.SafeHeightOffset = '3 mm' - obj.ClearanceHeightOffset = '5 mm' - obj.SafeHeightExpression = "StartDepth+%s.SafeHeightOffset" % self.expressionReference() - obj.ClearanceHeightExpression = "StartDepth+%s.ClearanceHeightOffset" % self.expressionReference() + obj.addProperty('App::PropertyString', 'StartDepthExpression', 'OperationDepths', translate('PathSetupSheet', 'Expression used for StartDepth of new operations.')) + obj.addProperty('App::PropertyString', 'FinalDepthExpression', 'OperationDepths', translate('PathSetupSheet', 'Expression used for FinalDepth of new operations.')) + obj.addProperty('App::PropertyString', 'StepDownExpression', 'OperationDepths', translate('PathSetupSheet', 'Expression used for StepDown of new operations.')) + + obj.SafeHeightOffset = self.decodeAttributeString(self.DefaultSafeHeightOffset) + obj.ClearanceHeightOffset = self.decodeAttributeString(self.DefaultClearanceHeightOffset) + obj.SafeHeightExpression = self.decodeAttributeString(self.DefaultSafeHeightExpression) + obj.ClearanceHeightExpression = self.decodeAttributeString(self.DefaultClearanceHeightExpression) + + obj.StartDepthExpression = self.decodeAttributeString(self.DefaultStartDepthExpression) + obj.FinalDepthExpression = self.decodeAttributeString(self.DefaultFinalDepthExpression) + obj.StepDownExpression = self.decodeAttributeString(self.DefaultStepDownExpression) obj.Proxy = self @@ -100,23 +126,37 @@ It's mostly a convencience wrapper around Spreadsheet. break return None + def hasDefaultToolRapids(self): + return PathGeom.isRoughly(self.obj.VertRapid.Value, 0) and PathGeom.isRoughly(self.obj.HorizRapid.Value, 0) + + def hasDefaultOperationHeights(self): + if self.obj.SafeHeightOffset.UserString != FreeCAD.Units.Quantity(self.DefaultSafeHeightOffset).UserString: + return False + if self.obj.ClearanceHeightOffset.UserString != FreeCAD.Units.Quantity(self.DefaultClearanceHeightOffset).UserString: + return False + if self.obj.SafeHeightExpression != self.decodeAttributeString(self.DefaultSafeHeightExpression): + return False + if self.obj.ClearanceHeightExpression != self.decodeAttributeString(self.DefaultClearanceHeightExpression): + return False + return True + + def hasDefaultOperationDepths(self): + if self.obj.StartDepthExpression != self.DefaultStartDepthExpression: + return False + if self.obj.FinalDepthExpression != self.DefaultFinalDepthExpression: + return False + if self.obj.StepDownExpression != self.DefaultStepDownExpression: + return False + return True + def setFromTemplate(self, attrs): '''setFromTemplate(attrs) ... sets the default values from the given dictionary.''' - if attrs.get(Template.VertRapid): - self.obj.VertRapid = attrs[Template.VertRapid] - if attrs.get(Template.HorizRapid): - self.obj.HorizRapid = attrs[Template.HorizRapid] - if attrs.get(Template.SafeHeightOffset): - self.obj.SafeHeightOffset = attrs[Template.SafeHeightOffset] - if attrs.get(Template.SafeHeightExpression): - self.obj.SafeHeightExpression = attrs[Template.SafeHeightExpression] - if attrs.get(Template.ClearanceHeightOffset): - self.obj.ClearanceHeightOffset = attrs[Template.ClearanceHeightOffset] - if attrs.get(Template.ClearanceHeightExpression): - self.obj.ClearanceHeightExpression = attrs[Template.ClearanceHeightExpression] + for name in Template.All: + if attrs.get(name) is not None: + setattr(self.obj, name, attrs[name]) - def templateAttributes(self, includeRapids=True, includeHeights=True): - '''templateAttributes(includeRapids, includeHeights) ... answers a dictionary with the default values.''' + def templateAttributes(self, includeRapids=True, includeHeights=True, includeDepths=True): + '''templateAttributes(includeRapids, includeHeights, includeDepths) ... answers a dictionary with the default values.''' attrs = {} if includeRapids: attrs[Template.VertRapid] = self.obj.VertRapid.UserString @@ -126,6 +166,10 @@ It's mostly a convencience wrapper around Spreadsheet. attrs[Template.SafeHeightExpression] = self.obj.SafeHeightExpression attrs[Template.ClearanceHeightOffset] = self.obj.ClearanceHeightOffset.UserString attrs[Template.ClearanceHeightExpression] = self.obj.ClearanceHeightExpression + if includeDepths: + attrs[Template.StartDepthExpression] = self.obj.StartDepthExpression + attrs[Template.FinalDepthExpression] = self.obj.FinalDepthExpression + attrs[Template.StepDownExpression] = self.obj.StepDownExpression return attrs def expressionReference(self): @@ -150,13 +194,20 @@ It's mostly a convencience wrapper around Spreadsheet. # https://forum.freecadweb.org/viewtopic.php?f=10&t=24845 return self.obj.Name + def encodeAttributeString(self, attr): + '''encodeAttributeString(attr) ... return the encoded string of a template attribute.''' + return unicode(attr.replace(self.expressionReference(), self.TemplateReference)) + def decodeAttributeString(self, attr): + '''decodeAttributeString(attr) ... return the decoded string of a template attribute.''' + return unicode(attr.replace(self.TemplateReference, self.expressionReference())) + def encodeTemplateAttributes(self, attrs): '''encodeTemplateAttributes(attrs) ... return a dictionary with all values encoded.''' - return _traverseTemplateAttributes(attrs, lambda value: unicode(value.replace(self.expressionReference(), self.TemplateReference))) + return _traverseTemplateAttributes(attrs, self.encodeAttributeString) def decodeTemplateAttributes(self, attrs): '''decodeTemplateAttributes(attrs) ... expand template attributes to reference the receiver where applicable.''' - return _traverseTemplateAttributes(attrs, lambda value: unicode(value.replace(self.TemplateReference, self.expressionReference()))) + return _traverseTemplateAttributes(attrs, self.decodeAttributeString) def Create(name='SetupSheet'): diff --git a/src/Mod/Path/PathScripts/PathStock.py b/src/Mod/Path/PathScripts/PathStock.py index ca22f697c9..a2ba938306 100644 --- a/src/Mod/Path/PathScripts/PathStock.py +++ b/src/Mod/Path/PathScripts/PathStock.py @@ -31,7 +31,7 @@ import math from PySide import QtCore -if True: +if False: PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) PathLog.trackModule(PathLog.thisModule()) diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index 304197551a..75ec8082bb 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -262,10 +262,8 @@ class ObjectSurface(PathOp.ObjectOp): 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 + obj.OpStartDepth = d.safe_height + obj.OpFinalDepth = d.start_depth def Create(name): '''Create(name) ... Creates and returns a Surface operation.''' diff --git a/src/Mod/Path/PathTests/TestPathSetupSheet.py b/src/Mod/Path/PathTests/TestPathSetupSheet.py index 2f04b4ea1d..c1d2e0b7dc 100644 --- a/src/Mod/Path/PathTests/TestPathSetupSheet.py +++ b/src/Mod/Path/PathTests/TestPathSetupSheet.py @@ -25,6 +25,9 @@ import FreeCAD import Path import PathScripts.PathSetupSheet as PathSetupSheet +import PathScripts.PathLog as PathLog + +PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) from PathTests.PathTestUtils import PathTestBase @@ -52,22 +55,23 @@ class TestPathSetupSheet(PathTestBase): def test01(self): '''Verify SetupSheet template attributes roundtrip.''' - ss = PathSetupSheet.Create().Proxy + o1 = PathSetupSheet.Create() self.doc.recompute() - ss.DefaultVertRapid = '10 mm/s' - ss.DefaultHorizRapid = '22 mm/s' - ss.DefaultSafeHeightOffset = '18 mm' - ss.DefaultSafeHeightExpression = 'Hugo+Olga' - ss.DefaultClearanceHeightOffset = '23 mm' - ss.DefaultClearanceHeightExpression = 'Peter+Paul' + o1.VertRapid = '10 mm/s' + o1.HorizRapid = '22 mm/s' + o1.SafeHeightOffset = '18 mm' + o1.SafeHeightExpression = 'Hugo+Olga' + o1.ClearanceHeightOffset = '23 mm' + o1.ClearanceHeightExpression = 'Peter+Paul' + o1.StartDepthExpression = 'Alpha' + o1.FinalDepthExpression = 'Omega' + o1.StepDownExpression = '1' - s2 = PathSetupSheet.Create().Proxy + o2 = PathSetupSheet.Create() self.doc.recompute() - s2.setFromTemplate(ss.templateAttributes()) + o2.Proxy.setFromTemplate(o1.Proxy.templateAttributes()) self.doc.recompute() - o1 = ss.obj - o2 = s2.obj self.assertRoughly(o1.VertRapid, o2.VertRapid) self.assertRoughly(o1.HorizRapid, o2.HorizRapid) @@ -75,6 +79,58 @@ class TestPathSetupSheet(PathTestBase): self.assertEqual(o1.SafeHeightExpression, o2.SafeHeightExpression) self.assertRoughly(o1.ClearanceHeightOffset, o2.ClearanceHeightOffset) self.assertEqual(o1.ClearanceHeightExpression, o2.ClearanceHeightExpression) + self.assertEqual(o1.StartDepthExpression, o2.StartDepthExpression) + self.assertEqual(o1.FinalDepthExpression, o2.FinalDepthExpression) + self.assertEqual(o1.StepDownExpression, o2.StepDownExpression) + + def test02(self): + '''Verify default value detection logic.''' + obj = PathSetupSheet.Create() + ss = obj.Proxy + + self.assertTrue(ss.hasDefaultToolRapids()) + self.assertTrue(ss.hasDefaultOperationHeights()) + self.assertTrue(ss.hasDefaultOperationDepths()) + + obj.VertRapid = '1 mm/s' + self.assertFalse(ss.hasDefaultToolRapids()) + obj.VertRapid = '0 mm/s' + self.assertTrue(ss.hasDefaultToolRapids()) + obj.HorizRapid = '1 mm/s' + self.assertFalse(ss.hasDefaultToolRapids()) + obj.HorizRapid = '0 mm/s' + self.assertTrue(ss.hasDefaultToolRapids()) + + obj.SafeHeightOffset = '0 mm' + self.assertFalse(ss.hasDefaultOperationHeights()) + obj.SafeHeightOffset = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultSafeHeightOffset) + self.assertTrue(ss.hasDefaultOperationHeights()) + obj.ClearanceHeightOffset = '0 mm' + self.assertFalse(ss.hasDefaultOperationHeights()) + obj.ClearanceHeightOffset = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultClearanceHeightOffset) + self.assertTrue(ss.hasDefaultOperationHeights()) + + obj.SafeHeightExpression = '0 mm' + self.assertFalse(ss.hasDefaultOperationHeights()) + obj.SafeHeightExpression = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultSafeHeightExpression) + self.assertTrue(ss.hasDefaultOperationHeights()) + obj.ClearanceHeightExpression = '0 mm' + self.assertFalse(ss.hasDefaultOperationHeights()) + obj.ClearanceHeightExpression = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultClearanceHeightExpression) + self.assertTrue(ss.hasDefaultOperationHeights()) + + obj.StartDepthExpression = '' + self.assertFalse(ss.hasDefaultOperationDepths()) + obj.StartDepthExpression = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultStartDepthExpression) + self.assertTrue(ss.hasDefaultOperationDepths()) + obj.FinalDepthExpression = '' + self.assertFalse(ss.hasDefaultOperationDepths()) + obj.FinalDepthExpression = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultFinalDepthExpression) + self.assertTrue(ss.hasDefaultOperationDepths()) + obj.StepDownExpression = '' + self.assertFalse(ss.hasDefaultOperationDepths()) + obj.StepDownExpression = ss.decodeAttributeString(PathSetupSheet.SetupSheet.DefaultStepDownExpression) + self.assertTrue(ss.hasDefaultOperationDepths()) def test10(self): '''Verify template attributes encoding/decoding of floats.'''