diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc index ef7eb16622..19875f199a 100644 --- a/src/Mod/Path/Gui/Resources/Path.qrc +++ b/src/Mod/Path/Gui/Resources/Path.qrc @@ -39,6 +39,7 @@ icons/Path-Profile.svg icons/Path-Sanity.svg icons/Path-SelectLoop.svg + icons/Path-SetupSheet.svg icons/Path-Shape.svg icons/Path-SimpleCopy.svg icons/Path-Speed.svg diff --git a/src/Mod/Path/Gui/Resources/icons/Path-SetupSheet.svg b/src/Mod/Path/Gui/Resources/icons/Path-SetupSheet.svg new file mode 100644 index 0000000000..f15eab6536 --- /dev/null +++ b/src/Mod/Path/Gui/Resources/icons/Path-SetupSheet.svg @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + [Eivind Kvedalen] + + + Spreadsheet + 2013-09-29 + http://www.freecadweb.org/wiki/index.php?title=Artwork + + + FreeCAD + + + FreeCAD/src/Mod/Spreadsheet/Gui/Resources/icons/Spreadsheet.svg + + + FreeCAD LGPL2+ + + + https://www.gnu.org/copyleft/lesser.html + + + [agryson] Alexander Gryson + + + + + + + + + + + + + + diff --git a/src/Mod/Path/PathScripts/PathJob.py b/src/Mod/Path/PathScripts/PathJob.py index 4bc342dc01..9f322fb3dc 100644 --- a/src/Mod/Path/PathScripts/PathJob.py +++ b/src/Mod/Path/PathScripts/PathJob.py @@ -137,10 +137,12 @@ class ObjectJob: obj.Stock.ViewObject.Visibility = False def setupSetupSheet(self, obj): - self.setupSheet = PathSetupSheet.SetupSheet(obj) if not hasattr(obj, 'SetupSheet'): obj.addProperty('App::PropertyLink', 'SetupSheet', 'Base', QtCore.QT_TRANSLATE_NOOP('PathJob', 'Spreadsheet holding the settings for this job')) - self.setupSheet.setup() + obj.SetupSheet = PathSetupSheet.Create() + if obj.SetupSheet.ViewObject: + PathIconViewProvider.ViewProvider(obj.SetupSheet.ViewObject, 'SetupSheet') + self.setupSheet = obj.SetupSheet.Proxy def onDelete(self, obj, arg2=None): '''Called by the view provider, there doesn't seem to be a callback on the obj itself.''' @@ -212,9 +214,9 @@ class ObjectJob: attrs = json.load(fp) if attrs.get(JobTemplate.Version) and 1 == int(attrs[JobTemplate.Version]): + attrs = self.setupSheet.decodeTemplateAttributes(attrs) if attrs.get(JobTemplate.SetupSheet): self.setupSheet.setFromTemplate(attrs[JobTemplate.SetupSheet]) - attrs = self.setupSheet.decodeTemplateAttributes(attrs) if attrs.get(JobTemplate.GeometryTolerance): obj.GeometryTolerance = float(attrs.get(JobTemplate.GeometryTolerance)) @@ -279,8 +281,8 @@ class ObjectJob: group = self.obj.ToolController PathLog.debug("addToolController(%s): %s" % (tc.Label, [t.Label for t in group])) if tc.Name not in [str(t.Name) for t in group]: - tc.setExpression('VertRapid', "%s.%s" % (self.setupSheet.expressionReference(), PathSetupSheet.Default.VertRapid)) - tc.setExpression('HorizRapid', "%s.%s" % (self.setupSheet.expressionReference(), PathSetupSheet.Default.HorizRapid)) + tc.setExpression('VertRapid', "%s.%s" % (self.setupSheet.expressionReference(), PathSetupSheet.Template.VertRapid)) + tc.setExpression('HorizRapid', "%s.%s" % (self.setupSheet.expressionReference(), PathSetupSheet.Template.HorizRapid)) group.append(tc) self.obj.ToolController = group diff --git a/src/Mod/Path/PathScripts/PathOp.py b/src/Mod/Path/PathScripts/PathOp.py index 463bd93adb..c00c8b15c7 100644 --- a/src/Mod/Path/PathScripts/PathOp.py +++ b/src/Mod/Path/PathScripts/PathOp.py @@ -255,8 +255,10 @@ class ObjectOp(object): obj.setExpression('StepDown', 'OpToolDiameter') if FeatureHeights & features: - obj.setExpression('SafeHeight', "%s.%s+StartDepth" % (job.Proxy.setupSheet.expressionReference(), PathSetupSheet.Default.SafeHeight)) - obj.setExpression('ClearanceHeight', "%s.%s+StartDepth" % (job.Proxy.setupSheet.expressionReference(), PathSetupSheet.Default.ClearanceHeight)) + if job.SetupSheet.SafeHeightExpression: + obj.setExpression('SafeHeight', job.SetupSheet.SafeHeightExpression) + if job.SetupSheet.ClearanceHeightExpression: + obj.setExpression('ClearanceHeight', job.SetupSheet.ClearanceHeightExpression) if FeatureStartPoint & features: obj.UseStartPoint = False diff --git a/src/Mod/Path/PathScripts/PathSetupSheet.py b/src/Mod/Path/PathScripts/PathSetupSheet.py index 19d7b530f8..b0a434eefd 100644 --- a/src/Mod/Path/PathScripts/PathSetupSheet.py +++ b/src/Mod/Path/PathScripts/PathSetupSheet.py @@ -41,11 +41,13 @@ else: def translate(context, text, disambig=None): return PySide.QtCore.QCoreApplication.translate(context, text, disambig) -class Default: - HorizRapid = 'DefaultHorizRapid' - VertRapid = 'DefaultVertRapid' - SafeHeight = 'DefaultSafeHeight' - ClearanceHeight = 'DefaultClearanceHeight' +class Template: + HorizRapid = 'HorizRapid' + VertRapid = 'VertRapid' + SafeHeightOffset = 'SafeHeight' + SafeHeightExpression = 'SafeHeightExpression' + ClearanceHeightOffset = 'ClearanceHeight' + ClearanceHeightExpression = 'ClearanceHeightExpression' def _traverseTemplateAttributes(attrs, codec): coded = {} @@ -73,56 +75,57 @@ It's mostly a convencience wrapper around Spreadsheet. def __init__(self, obj): self.obj = obj + obj.addProperty('App::PropertySpeed', 'VertRapid', 'ToolController', translate('PathSetupSheet', 'Default speed for horizontal rapid moves.')) + obj.addProperty('App::PropertySpeed', 'HorizRapid', 'ToolController', translate('PathSetupSheet', 'Default speed for vertical rapid moves.')) - def setup(self, label='SetupSheet'): - '''setup(label=None) ... initializes receiver with default values.''' - self.obj.SetupSheet = self.obj.Document.addObject('Spreadsheet::Sheet', 'Spreadsheet') - if label: - self.obj.SetupSheet.Label = label - self.obj.SetupSheet.set('A2', translate('PathSetupSheet', 'Tool Rapid Speeds')) - self.createSetting(3, Default.HorizRapid, '0 mm/s', translate('PathSetupSheet', 'Horizontal'), translate('PathSetupSheet', 'Default speed for horizzontal rapid moves.')) - self.createSetting(4, Default.VertRapid, '0 mm/s', translate('PathSetupSheet', 'Vertical'), translate('PathSetupSheet', 'Default speed for vertical rapid moves.')) - self.obj.SetupSheet.set('A6', translate('PathSetupSheet', 'Operation Heights')) - self.createSetting(7, Default.SafeHeight, '3 mm', translate('PathSetupSheet', 'Safe Height'), translate('PathSetupSheet', 'Default value added to StartDepth used for the safe height.')) - self.createSetting(8, Default.ClearanceHeight, '5 mm', translate('PathSetupSheet', 'Clearance Height'), translate('PathSetupSheet', 'Default value added to StartDepth used for the clearance height.')) + obj.addProperty('App::PropertyLength', 'SafeHeightOffset', 'OperationHeights', translate('PathSetupSheet', 'The usage of this field depends on SafeHeightExpression - by default its value is added to StartDepth and used for SafeHeight of an operation.')) + obj.addProperty('App::PropertyString', 'SafeHeightExpression', 'OperationHeights', translate('PathSetupSheet', 'Expression set for the SafeHeight of new operations.')) + 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.')) - def updateSetting(self, alias, value): - '''updateSetting(alias, value) ... stores a new value for the given value.''' - cell = self.obj.SetupSheet.getCellFromAlias(alias) - PathLog.debug("updateSetting(%s, %s): %s" % (alias, value, cell)) - self.obj.SetupSheet.set(cell, value) + obj.SafeHeightOffset = '3 mm' + obj.ClearanceHeightOffset = '5 mm' + obj.SafeHeightExpression = "StartDepth+%s.SafeHeightOffset" % self.expressionReference() + obj.ClearanceHeightExpression = "StartDepth+%s.ClearanceHeightOffset" % self.expressionReference() - def createSetting(self, row, alias, value, label, desc): - '''createSetting(row, alias, value, label, desc) ... sets the values of the new setting in the given row.''' - labelCell = "B%d" % row - valueCell = "C%d" % row - descCell = "D%d" % row - PathLog.debug("createSetting(%d, %s, %s): %s" % (row, alias, value, valueCell)) - self.obj.SetupSheet.set(labelCell, label) - self.obj.SetupSheet.set(valueCell, value) - self.obj.SetupSheet.setAlias(valueCell, alias) - self.obj.SetupSheet.set(descCell, desc) + obj.Proxy = self + + def __getstate__(self): + return None + + def __setstate__(self, state): + for obj in FreeCAD.ActiveDocument.Objects: + if hasattr(obj, 'Proxy') and obj.Proxy == self: + self.obj = obj + break + return None def setFromTemplate(self, attrs): '''setFromTemplate(attrs) ... sets the default values from the given dictionary.''' - if attrs.get(Default.VertRapid): - self.updateSetting(Default.VertRapid, attrs[Default.VertRapid]) - if attrs.get(Default.HorizRapid): - self.updateSetting(Default.HorizRapid, attrs[Default.HorizRapid]) - if attrs.get(Default.SafeHeight): - self.updateSetting(Default.SafeHeight, attrs[Default.SafeHeight]) - if attrs.get(Default.ClearanceHeight): - self.updateSetting(Default.ClearanceHeight, attrs[Default.ClearanceHeight]) + 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] def templateAttributes(self, includeRapids=True, includeHeights=True): '''templateAttributes(includeRapids, includeHeights) ... answers a dictionary with the default values.''' attrs = {} if includeRapids: - attrs[Default.VertRapid] = self.obj.SetupSheet.DefaultVertRapid.UserString - attrs[Default.HorizRapid] = self.obj.SetupSheet.DefaultHorizRapid.UserString + attrs[Template.VertRapid] = self.obj.VertRapid.UserString + attrs[Template.HorizRapid] = self.obj.HorizRapid.UserString if includeHeights: - attrs[Default.SafeHeight] = self.obj.SetupSheet.DefaultSafeHeight.UserString - attrs[Default.ClearanceHeight] = self.obj.SetupSheet.DefaultClearanceHeight.UserString + attrs[Template.SafeHeightOffset] = self.obj.SafeHeightOffset.UserString + attrs[Template.SafeHeightExpression] = self.obj.SafeHeightExpression + attrs[Template.ClearanceHeightOffset] = self.obj.ClearanceHeightOffset.UserString + attrs[Template.ClearanceHeightExpression] = self.obj.ClearanceHeightExpression return attrs def expressionReference(self): @@ -145,7 +148,7 @@ It's mostly a convencience wrapper around Spreadsheet. # # https://forum.freecadweb.org/viewtopic.php?f=10&t=24839 # https://forum.freecadweb.org/viewtopic.php?f=10&t=24845 - return self.obj.SetupSheet.Name + return self.obj.Name def encodeTemplateAttributes(self, attrs): '''encodeTemplateAttributes(attrs) ... return a dictionary with all values encoded.''' @@ -154,3 +157,9 @@ It's mostly a convencience wrapper around Spreadsheet. 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()))) + + +def Create(name='SetupSheet'): + obj = FreeCAD.ActiveDocument.addObject('App::FeaturePython', name) + proxy = SetupSheet(obj) + return obj diff --git a/src/Mod/Path/PathTests/TestPathSetupSheet.py b/src/Mod/Path/PathTests/TestPathSetupSheet.py index 57a08967b8..2f04b4ea1d 100644 --- a/src/Mod/Path/PathTests/TestPathSetupSheet.py +++ b/src/Mod/Path/PathTests/TestPathSetupSheet.py @@ -29,98 +29,94 @@ import PathScripts.PathSetupSheet as PathSetupSheet from PathTests.PathTestUtils import PathTestBase class TestPathSetupSheet(PathTestBase): - class TestObject: - def __init__(self, doc): - self.Document = doc - self.SetupSheet = None def setUp(self): self.doc = FreeCAD.newDocument("TestPathSetupSheet") - self.obj = self.TestObject(self.doc) def tearDown(self): FreeCAD.closeDocument(self.doc.Name) def test00(self): '''Verify SetupSheet templateAttributes''' - ss = PathSetupSheet.SetupSheet(self.obj) - ss.setup() + ss = PathSetupSheet.Create().Proxy self.doc.recompute() attrs = ss.templateAttributes(True, True) - self.assertEqual(attrs[PathSetupSheet.Default.HorizRapid], '0.00 mm/s') - self.assertEqual(attrs[PathSetupSheet.Default.VertRapid], '0.00 mm/s') - self.assertEqual(attrs[PathSetupSheet.Default.SafeHeight], '3.00 mm') - self.assertEqual(attrs[PathSetupSheet.Default.ClearanceHeight], '5.00 mm') + self.assertEqual(attrs[PathSetupSheet.Template.HorizRapid], '0.00 mm/s') + self.assertEqual(attrs[PathSetupSheet.Template.VertRapid], '0.00 mm/s') + self.assertEqual(attrs[PathSetupSheet.Template.SafeHeightOffset], '3.00 mm') + self.assertEqual(attrs[PathSetupSheet.Template.SafeHeightExpression], 'StartDepth+SetupSheet.SafeHeightOffset') + self.assertEqual(attrs[PathSetupSheet.Template.ClearanceHeightOffset], '5.00 mm') + self.assertEqual(attrs[PathSetupSheet.Template.ClearanceHeightExpression], 'StartDepth+SetupSheet.ClearanceHeightOffset') def test01(self): '''Verify SetupSheet template attributes roundtrip.''' - ss = PathSetupSheet.SetupSheet(self.obj) - ss.setup() + ss = PathSetupSheet.Create().Proxy self.doc.recompute() ss.DefaultVertRapid = '10 mm/s' ss.DefaultHorizRapid = '22 mm/s' - ss.DefaultSafeHeight = '18 mm' - ss.DefaultClearanceHeight = '23 mm' + ss.DefaultSafeHeightOffset = '18 mm' + ss.DefaultSafeHeightExpression = 'Hugo+Olga' + ss.DefaultClearanceHeightOffset = '23 mm' + ss.DefaultClearanceHeightExpression = 'Peter+Paul' - o2 = self.TestObject(self.doc) - s2 = PathSetupSheet.SetupSheet(o2) - s2.setup() + s2 = PathSetupSheet.Create().Proxy self.doc.recompute() s2.setFromTemplate(ss.templateAttributes()) self.doc.recompute() - self.assertRoughly(self.obj.SetupSheet.DefaultVertRapid, o2.SetupSheet.DefaultVertRapid) - self.assertRoughly(self.obj.SetupSheet.DefaultHorizRapid, o2.SetupSheet.DefaultHorizRapid) - self.assertRoughly(self.obj.SetupSheet.DefaultSafeHeight, o2.SetupSheet.DefaultSafeHeight) - self.assertRoughly(self.obj.SetupSheet.DefaultClearanceHeight, o2.SetupSheet.DefaultClearanceHeight) + o1 = ss.obj + o2 = s2.obj + + self.assertRoughly(o1.VertRapid, o2.VertRapid) + self.assertRoughly(o1.HorizRapid, o2.HorizRapid) + self.assertRoughly(o1.SafeHeightOffset, o2.SafeHeightOffset) + self.assertEqual(o1.SafeHeightExpression, o2.SafeHeightExpression) + self.assertRoughly(o1.ClearanceHeightOffset, o2.ClearanceHeightOffset) + self.assertEqual(o1.ClearanceHeightExpression, o2.ClearanceHeightExpression) def test10(self): '''Verify template attributes encoding/decoding of floats.''' - ss = PathSetupSheet.SetupSheet(self.obj) - ss.setup('x') + ss = PathSetupSheet.Create().Proxy - self.assertEqual(ss.expressionReference(), 'Spreadsheet') + self.assertEqual(ss.expressionReference(), 'SetupSheet') self.assertEqual(str(ss.encodeTemplateAttributes({'00': 13.00})), "{'00': 13.0}") self.assertEqual(str(ss.decodeTemplateAttributes({'00': 13.00})), "{'00': 13.0}") def test11(self): '''Verify template attributes encoding/decoding of strings.''' - ss = PathSetupSheet.SetupSheet(self.obj) - ss.setup('x') + ss = PathSetupSheet.Create().Proxy - self.assertEqual(str(ss.encodeTemplateAttributes({'00': 'SetupSheet'})), "{'00': u'SetupSheet'}") - self.assertEqual(str(ss.encodeTemplateAttributes({'00': 'Spreadsheet'})), "{'00': u'${SetupSheet}'}") - self.assertEqual(str(ss.encodeTemplateAttributes({'00': 'Spreadsheet.y'})), "{'00': u'${SetupSheet}.y'}") - self.assertEqual(str(ss.encodeTemplateAttributes({'00': '${SetupSheet}'})), "{'00': u'${SetupSheet}'}") + self.assertEqual(str(ss.encodeTemplateAttributes({'00': 'hugo'})), "{'00': u'hugo'}") + self.assertEqual(str(ss.encodeTemplateAttributes({'00': 'SetupSheet'})), "{'00': u'${SetupSheet}'}") + self.assertEqual(str(ss.encodeTemplateAttributes({'00': 'SetupSheet.y'})), "{'00': u'${SetupSheet}.y'}") + self.assertEqual(str(ss.encodeTemplateAttributes({'00': '${hugo}'})), "{'00': u'${hugo}'}") - self.assertEqual(str(ss.decodeTemplateAttributes({'00': 'SetupSheet'})), "{'00': u'SetupSheet'}") - self.assertEqual(str(ss.decodeTemplateAttributes({'00': '${SetupSheet}'})), "{'00': u'Spreadsheet'}") - self.assertEqual(str(ss.decodeTemplateAttributes({'00': '${SetupSheet}.y'})), "{'00': u'Spreadsheet.y'}") - self.assertEqual(str(ss.decodeTemplateAttributes({'00': '${SetupSheet}.y - ${SetupSheet}.z'})), "{'00': u'Spreadsheet.y - Spreadsheet.z'}") + self.assertEqual(str(ss.decodeTemplateAttributes({'00': 'hugo'})), "{'00': u'hugo'}") + self.assertEqual(str(ss.decodeTemplateAttributes({'00': '${SetupSheet}'})), "{'00': u'SetupSheet'}") + self.assertEqual(str(ss.decodeTemplateAttributes({'00': '${SetupSheet}.y'})), "{'00': u'SetupSheet.y'}") + self.assertEqual(str(ss.decodeTemplateAttributes({'00': '${SetupSheet}.y - ${SetupSheet}.z'})), "{'00': u'SetupSheet.y - SetupSheet.z'}") def test12(self): '''Verify template attributes encoding/decoding of dictionaries.''' - ss = PathSetupSheet.SetupSheet(self.obj) - ss.setup('rrr') + ss = PathSetupSheet.Create().Proxy - self.assertEqual(str(ss.encodeTemplateAttributes({'00': {'01': 'SetupSheet'}})), "{'00': {'01': u'SetupSheet'}}") - self.assertEqual(str(ss.encodeTemplateAttributes({'00': {'01': 'Spreadsheet.y - Spreadsheet.z'}})), "{'00': {'01': u'${SetupSheet}.y - ${SetupSheet}.z'}}") + self.assertEqual(str(ss.encodeTemplateAttributes({'00': {'01': 'hugo'}})), "{'00': {'01': u'hugo'}}") + self.assertEqual(str(ss.encodeTemplateAttributes({'00': {'01': 'SetupSheet.y - SetupSheet.z'}})), "{'00': {'01': u'${SetupSheet}.y - ${SetupSheet}.z'}}") - self.assertEqual(str(ss.decodeTemplateAttributes({'00': {'01': 'SetupSheet'}})), "{'00': {'01': u'SetupSheet'}}") - self.assertEqual(str(ss.decodeTemplateAttributes({'00': {'01': '${SetupSheet}.y - ${SetupSheet}.z'}})), "{'00': {'01': u'Spreadsheet.y - Spreadsheet.z'}}") + self.assertEqual(str(ss.decodeTemplateAttributes({'00': {'01': 'hugo'}})), "{'00': {'01': u'hugo'}}") + self.assertEqual(str(ss.decodeTemplateAttributes({'00': {'01': '${SetupSheet}.y - ${SetupSheet}.z'}})), "{'00': {'01': u'SetupSheet.y - SetupSheet.z'}}") def test13(self): '''Verify template attributes encoding/decoding of lists.''' - ss = PathSetupSheet.SetupSheet(self.obj) - ss.setup('hugo') + ss = PathSetupSheet.Create().Proxy attrs = {} - attrs['00'] = 'x.Spreadsheet' - attrs['01'] = [{'10': 'Spreadsheet', '11': 'Spreadsheet.y'}, {'20': 'Spreadsheet'}] - attrs['02'] = [{'a': [{'b': 'Spreadsheet'}, {'c': 'Spreadsheet'}], 'b': [{'b': 'Spreadsheet'}]}] + attrs['00'] = 'x.SetupSheet' + attrs['01'] = [{'10': 'SetupSheet', '11': 'SetupSheet.y'}, {'20': 'SetupSheet'}] + attrs['02'] = [{'a': [{'b': 'SetupSheet'}, {'c': 'SetupSheet'}], 'b': [{'b': 'SetupSheet'}]}] encoded = ss.encodeTemplateAttributes(attrs) self.assertEqual(encoded['00'], 'x.${SetupSheet}') @@ -150,14 +146,11 @@ class TestPathSetupSheet(PathTestBase): self.assertEqual(decoded['02'][0]['b'][0]['b'], attrs['02'][0]['b'][0]['b']) # just to be safe ... - o2 = self.TestObject(self.doc) - s2 = PathSetupSheet.SetupSheet(o2) - s2.setup() + s2 = PathSetupSheet.Create().Proxy self.doc.recompute() s2.setFromTemplate(ss.templateAttributes()) - o2.SetupSheet.Label = 'xxx' - self.assertEqual(s2.expressionReference(), 'Spreadsheet001') + self.assertEqual(s2.expressionReference(), 'SetupSheet001') dec = s2.decodeTemplateAttributes(encoded) # pick one - self.assertEqual(dec['01'][0]['11'], 'Spreadsheet001.y') + self.assertEqual(dec['01'][0]['11'], 'SetupSheet001.y')