Replaced spreadsheet with dedicated SetupSheet object in order to better support expression setting.

This commit is contained in:
Markus Lampert
2017-10-12 22:27:24 -07:00
parent a61ee41695
commit e2b48fa229
6 changed files with 174 additions and 103 deletions

View File

@@ -39,6 +39,7 @@
<file>icons/Path-Profile.svg</file>
<file>icons/Path-Sanity.svg</file>
<file>icons/Path-SelectLoop.svg</file>
<file>icons/Path-SetupSheet.svg</file>
<file>icons/Path-Shape.svg</file>
<file>icons/Path-SimpleCopy.svg</file>
<file>icons/Path-Speed.svg</file>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

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

View File

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

View File

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

View File

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