purge archpanel support
This commit is contained in:
@@ -29,10 +29,10 @@ from PySide import QtCore
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
ArchPanel = LazyLoader('ArchPanel', globals(), 'ArchPanel')
|
||||
Draft = LazyLoader('Draft', globals(), 'Draft')
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
DraftGeomUtils = LazyLoader('DraftGeomUtils', globals(), 'DraftGeomUtils')
|
||||
|
||||
Draft = LazyLoader("Draft", globals(), "Draft")
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
DraftGeomUtils = LazyLoader("DraftGeomUtils", globals(), "DraftGeomUtils")
|
||||
|
||||
|
||||
__title__ = "Path Circular Holes Base Operation"
|
||||
@@ -51,81 +51,70 @@ PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
|
||||
class ObjectOp(PathOp.ObjectOp):
|
||||
'''Base class for proxy objects of all operations on circular holes.'''
|
||||
"""Base class for proxy objects of all operations on circular holes."""
|
||||
|
||||
def opFeatures(self, obj):
|
||||
'''opFeatures(obj) ... calls circularHoleFeatures(obj) and ORs in the standard features required for processing circular holes.
|
||||
Do not overwrite, implement circularHoleFeatures(obj) instead'''
|
||||
return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureHeights \
|
||||
| PathOp.FeatureBaseFaces | self.circularHoleFeatures(obj) \
|
||||
"""opFeatures(obj) ... calls circularHoleFeatures(obj) and ORs in the standard features required for processing circular holes.
|
||||
Do not overwrite, implement circularHoleFeatures(obj) instead"""
|
||||
return (
|
||||
PathOp.FeatureTool
|
||||
| PathOp.FeatureDepths
|
||||
| PathOp.FeatureHeights
|
||||
| PathOp.FeatureBaseFaces
|
||||
| self.circularHoleFeatures(obj)
|
||||
| PathOp.FeatureCoolant
|
||||
)
|
||||
|
||||
def circularHoleFeatures(self, obj):
|
||||
'''circularHoleFeatures(obj) ... overwrite to add operations specific features.
|
||||
Can safely be overwritten by subclasses.'''
|
||||
"""circularHoleFeatures(obj) ... overwrite to add operations specific features.
|
||||
Can safely be overwritten by subclasses."""
|
||||
return 0
|
||||
|
||||
def initOperation(self, obj):
|
||||
'''initOperation(obj) ... adds Disabled properties and calls initCircularHoleOperation(obj).
|
||||
Do not overwrite, implement initCircularHoleOperation(obj) instead.'''
|
||||
obj.addProperty("App::PropertyStringList", "Disabled", "Base", QtCore.QT_TRANSLATE_NOOP("Path", "List of disabled features"))
|
||||
"""initOperation(obj) ... adds Disabled properties and calls initCircularHoleOperation(obj).
|
||||
Do not overwrite, implement initCircularHoleOperation(obj) instead."""
|
||||
obj.addProperty(
|
||||
"App::PropertyStringList",
|
||||
"Disabled",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP("Path", "List of disabled features"),
|
||||
)
|
||||
self.initCircularHoleOperation(obj)
|
||||
|
||||
def initCircularHoleOperation(self, obj):
|
||||
'''initCircularHoleOperation(obj) ... overwrite if the subclass needs initialisation.
|
||||
Can safely be overwritten by subclasses.'''
|
||||
"""initCircularHoleOperation(obj) ... overwrite if the subclass needs initialisation.
|
||||
Can safely be overwritten by subclasses."""
|
||||
pass
|
||||
|
||||
def baseIsArchPanel(self, obj, base):
|
||||
'''baseIsArchPanel(obj, base) ... return true if op deals with an Arch.Panel.'''
|
||||
return hasattr(base, "Proxy") and isinstance(base.Proxy, ArchPanel.PanelSheet)
|
||||
|
||||
def getArchPanelEdge(self, obj, base, sub):
|
||||
'''getArchPanelEdge(obj, base, sub) ... helper function to identify a specific edge of an Arch.Panel.
|
||||
Edges are identified by 3 numbers:
|
||||
<holeId>.<wireId>.<edgeId>
|
||||
Let's say the edge is specified as "3.2.7", then the 7th edge of the 2nd wire in the 3rd hole returned
|
||||
by the panel sheet is the edge returned.
|
||||
Obviously this is as fragile as can be, but currently the best we can do while the panel sheets
|
||||
hide the actual features from Path and they can't be referenced directly.
|
||||
'''
|
||||
ids = sub.split(".")
|
||||
holeId = int(ids[0])
|
||||
wireId = int(ids[1])
|
||||
edgeId = int(ids[2])
|
||||
|
||||
for holeNr, hole in enumerate(base.Proxy.getHoles(base, transform=True)):
|
||||
if holeNr == holeId:
|
||||
for wireNr, wire in enumerate(hole.Wires):
|
||||
if wireNr == wireId:
|
||||
for edgeNr, edge in enumerate(wire.Edges):
|
||||
if edgeNr == edgeId:
|
||||
return edge
|
||||
|
||||
def holeDiameter(self, obj, base, sub):
|
||||
'''holeDiameter(obj, base, sub) ... returns the diameter of the specified hole.'''
|
||||
if self.baseIsArchPanel(obj, base):
|
||||
edge = self.getArchPanelEdge(obj, base, sub)
|
||||
return edge.BoundBox.XLength
|
||||
|
||||
"""holeDiameter(obj, base, sub) ... returns the diameter of the specified hole."""
|
||||
try:
|
||||
shape = base.Shape.getElement(sub)
|
||||
if shape.ShapeType == 'Vertex':
|
||||
if shape.ShapeType == "Vertex":
|
||||
return 0
|
||||
|
||||
if shape.ShapeType == 'Edge' and type(shape.Curve) == Part.Circle:
|
||||
if shape.ShapeType == "Edge" and type(shape.Curve) == Part.Circle:
|
||||
return shape.Curve.Radius * 2
|
||||
|
||||
if shape.ShapeType == 'Face':
|
||||
if shape.ShapeType == "Face":
|
||||
for i in range(len(shape.Edges)):
|
||||
if (type(shape.Edges[i].Curve) == Part.Circle and
|
||||
shape.Edges[i].Curve.Radius * 2 < shape.BoundBox.XLength*1.1 and
|
||||
shape.Edges[i].Curve.Radius * 2 > shape.BoundBox.XLength*0.9):
|
||||
if (
|
||||
type(shape.Edges[i].Curve) == Part.Circle
|
||||
and shape.Edges[i].Curve.Radius * 2
|
||||
< shape.BoundBox.XLength * 1.1
|
||||
and shape.Edges[i].Curve.Radius * 2
|
||||
> shape.BoundBox.XLength * 0.9
|
||||
):
|
||||
return shape.Edges[i].Curve.Radius * 2
|
||||
|
||||
# for all other shapes the diameter is just the dimension in X.
|
||||
# This may be inaccurate as the BoundBox is calculated on the tessellated geometry
|
||||
PathLog.warning(translate("Path", "Hole diameter may be inaccurate due to tessellation on face. Consider selecting hole edge."))
|
||||
PathLog.warning(
|
||||
translate(
|
||||
"Path",
|
||||
"Hole diameter may be inaccurate due to tessellation on face. Consider selecting hole edge.",
|
||||
)
|
||||
)
|
||||
return shape.BoundBox.XLength
|
||||
except Part.OCCError as e:
|
||||
PathLog.error(e)
|
||||
@@ -133,44 +122,48 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
return 0
|
||||
|
||||
def holePosition(self, obj, base, sub):
|
||||
'''holePosition(obj, base, sub) ... returns a Vector for the position defined by the given features.
|
||||
Note that the value for Z is set to 0.'''
|
||||
if self.baseIsArchPanel(obj, base):
|
||||
edge = self.getArchPanelEdge(obj, base, sub)
|
||||
center = edge.Curve.Center
|
||||
return FreeCAD.Vector(center.x, center.y, 0)
|
||||
"""holePosition(obj, base, sub) ... returns a Vector for the position defined by the given features.
|
||||
Note that the value for Z is set to 0."""
|
||||
|
||||
try:
|
||||
shape = base.Shape.getElement(sub)
|
||||
if shape.ShapeType == 'Vertex':
|
||||
if shape.ShapeType == "Vertex":
|
||||
return FreeCAD.Vector(shape.X, shape.Y, 0)
|
||||
|
||||
if shape.ShapeType == 'Edge' and hasattr(shape.Curve, 'Center'):
|
||||
if shape.ShapeType == "Edge" and hasattr(shape.Curve, "Center"):
|
||||
return FreeCAD.Vector(shape.Curve.Center.x, shape.Curve.Center.y, 0)
|
||||
|
||||
if shape.ShapeType == 'Face':
|
||||
if hasattr(shape.Surface, 'Center'):
|
||||
return FreeCAD.Vector(shape.Surface.Center.x, shape.Surface.Center.y, 0)
|
||||
if shape.ShapeType == "Face":
|
||||
if hasattr(shape.Surface, "Center"):
|
||||
return FreeCAD.Vector(
|
||||
shape.Surface.Center.x, shape.Surface.Center.y, 0
|
||||
)
|
||||
if len(shape.Edges) == 1 and type(shape.Edges[0].Curve) == Part.Circle:
|
||||
return shape.Edges[0].Curve.Center
|
||||
except Part.OCCError as e:
|
||||
PathLog.error(e)
|
||||
|
||||
PathLog.error(translate("Path", "Feature %s.%s cannot be processed as a circular hole - please remove from Base geometry list.") % (base.Label, sub))
|
||||
PathLog.error(
|
||||
translate(
|
||||
"Path",
|
||||
"Feature %s.%s cannot be processed as a circular hole - please remove from Base geometry list.",
|
||||
)
|
||||
% (base.Label, sub)
|
||||
)
|
||||
return None
|
||||
|
||||
def isHoleEnabled(self, obj, base, sub):
|
||||
'''isHoleEnabled(obj, base, sub) ... return true if hole is enabled.'''
|
||||
"""isHoleEnabled(obj, base, sub) ... return true if hole is enabled."""
|
||||
name = "%s.%s" % (base.Name, sub)
|
||||
return not name in obj.Disabled
|
||||
return name not in obj.Disabled
|
||||
|
||||
def opExecute(self, obj):
|
||||
'''opExecute(obj) ... processes all Base features and Locations and collects
|
||||
"""opExecute(obj) ... processes all Base features and Locations and collects
|
||||
them in a list of positions and radii which is then passed to circularHoleExecute(obj, holes).
|
||||
If no Base geometries and no Locations are present, the job's Base is inspected and all
|
||||
drillable features are added to Base. In this case appropriate values for depths are also
|
||||
calculated and assigned.
|
||||
Do not overwrite, implement circularHoleExecute(obj, holes) instead.'''
|
||||
Do not overwrite, implement circularHoleExecute(obj, holes) instead."""
|
||||
PathLog.track()
|
||||
|
||||
def haveLocations(self, obj):
|
||||
@@ -182,80 +175,91 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
|
||||
for base, subs in obj.Base:
|
||||
for sub in subs:
|
||||
PathLog.debug('processing {} in {}'.format(sub, base.Name))
|
||||
PathLog.debug("processing {} in {}".format(sub, base.Name))
|
||||
if self.isHoleEnabled(obj, base, sub):
|
||||
pos = self.holePosition(obj, base, sub)
|
||||
if pos:
|
||||
holes.append({'x': pos.x, 'y': pos.y, 'r': self.holeDiameter(obj, base, sub)})
|
||||
holes.append(
|
||||
{
|
||||
"x": pos.x,
|
||||
"y": pos.y,
|
||||
"r": self.holeDiameter(obj, base, sub),
|
||||
}
|
||||
)
|
||||
|
||||
if haveLocations(self, obj):
|
||||
for location in obj.Locations:
|
||||
holes.append({'x': location.x, 'y': location.y, 'r': 0})
|
||||
holes.append({"x": location.x, "y": location.y, "r": 0})
|
||||
|
||||
if len(holes) > 0:
|
||||
self.circularHoleExecute(obj, holes)
|
||||
|
||||
def circularHoleExecute(self, obj, holes):
|
||||
'''circularHoleExecute(obj, holes) ... implement processing of holes.
|
||||
"""circularHoleExecute(obj, holes) ... implement processing of holes.
|
||||
holes is a list of dictionaries with 'x', 'y' and 'r' specified for each hole.
|
||||
Note that for Vertexes, non-circular Edges and Locations r=0.
|
||||
Must be overwritten by subclasses.'''
|
||||
Must be overwritten by subclasses."""
|
||||
pass
|
||||
|
||||
def findAllHoles(self, obj):
|
||||
'''findAllHoles(obj) ... find all holes of all base models and assign as features.'''
|
||||
"""findAllHoles(obj) ... find all holes of all base models and assign as features."""
|
||||
PathLog.track()
|
||||
if not self.getJob(obj):
|
||||
return
|
||||
features = []
|
||||
if 1 == len(self.model) and self.baseIsArchPanel(obj, self.model[0]):
|
||||
panel = self.model[0]
|
||||
holeshapes = panel.Proxy.getHoles(panel, transform=True)
|
||||
tooldiameter = float(obj.ToolController.Proxy.getTool(obj.ToolController).Diameter)
|
||||
for holeNr, hole in enumerate(holeshapes):
|
||||
PathLog.debug('Entering new HoleShape')
|
||||
for wireNr, wire in enumerate(hole.Wires):
|
||||
PathLog.debug('Entering new Wire')
|
||||
for edgeNr, edge in enumerate(wire.Edges):
|
||||
if PathUtils.isDrillable(panel, edge, tooldiameter):
|
||||
PathLog.debug('Found drillable hole edges: {}'.format(edge))
|
||||
features.append((panel, "%d.%d.%d" % (holeNr, wireNr, edgeNr)))
|
||||
else:
|
||||
for base in self.model:
|
||||
features.extend(self.findHoles(obj, base))
|
||||
for base in self.model:
|
||||
features.extend(self.findHoles(obj, base))
|
||||
obj.Base = features
|
||||
obj.Disabled = []
|
||||
|
||||
def findHoles(self, obj, baseobject):
|
||||
'''findHoles(obj, baseobject) ... inspect baseobject and identify all features that resemble a straight cricular hole.'''
|
||||
"""findHoles(obj, baseobject) ... inspect baseobject and identify all features that resemble a straight cricular hole."""
|
||||
shape = baseobject.Shape
|
||||
PathLog.track('obj: {} shape: {}'.format(obj, shape))
|
||||
PathLog.track("obj: {} shape: {}".format(obj, shape))
|
||||
holelist = []
|
||||
features = []
|
||||
# tooldiameter = float(obj.ToolController.Proxy.getTool(obj.ToolController).Diameter)
|
||||
tooldiameter = None
|
||||
PathLog.debug('search for holes larger than tooldiameter: {}: '.format(tooldiameter))
|
||||
PathLog.debug(
|
||||
"search for holes larger than tooldiameter: {}: ".format(tooldiameter)
|
||||
)
|
||||
if DraftGeomUtils.isPlanar(shape):
|
||||
PathLog.debug("shape is planar")
|
||||
for i in range(len(shape.Edges)):
|
||||
candidateEdgeName = "Edge" + str(i + 1)
|
||||
e = shape.getElement(candidateEdgeName)
|
||||
if PathUtils.isDrillable(shape, e, tooldiameter):
|
||||
PathLog.debug('edge candidate: {} (hash {})is drillable '.format(e, e.hashCode()))
|
||||
PathLog.debug(
|
||||
"edge candidate: {} (hash {})is drillable ".format(
|
||||
e, e.hashCode()
|
||||
)
|
||||
)
|
||||
x = e.Curve.Center.x
|
||||
y = e.Curve.Center.y
|
||||
diameter = e.BoundBox.XLength
|
||||
holelist.append({'featureName': candidateEdgeName, 'feature': e, 'x': x, 'y': y, 'd': diameter, 'enabled': True})
|
||||
holelist.append(
|
||||
{
|
||||
"featureName": candidateEdgeName,
|
||||
"feature": e,
|
||||
"x": x,
|
||||
"y": y,
|
||||
"d": diameter,
|
||||
"enabled": True,
|
||||
}
|
||||
)
|
||||
features.append((baseobject, candidateEdgeName))
|
||||
PathLog.debug("Found hole feature %s.%s" % (baseobject.Label, candidateEdgeName))
|
||||
PathLog.debug(
|
||||
"Found hole feature %s.%s"
|
||||
% (baseobject.Label, candidateEdgeName)
|
||||
)
|
||||
else:
|
||||
PathLog.debug("shape is not planar")
|
||||
for i in range(len(shape.Faces)):
|
||||
candidateFaceName = "Face" + str(i + 1)
|
||||
f = shape.getElement(candidateFaceName)
|
||||
if PathUtils.isDrillable(shape, f, tooldiameter):
|
||||
PathLog.debug('face candidate: {} is drillable '.format(f))
|
||||
if hasattr(f.Surface, 'Center'):
|
||||
PathLog.debug("face candidate: {} is drillable ".format(f))
|
||||
if hasattr(f.Surface, "Center"):
|
||||
x = f.Surface.Center.x
|
||||
y = f.Surface.Center.y
|
||||
diameter = f.BoundBox.XLength
|
||||
@@ -264,9 +268,21 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
x = center.x
|
||||
y = center.y
|
||||
diameter = f.Edges[0].Curve.Radius * 2
|
||||
holelist.append({'featureName': candidateFaceName, 'feature': f, 'x': x, 'y': y, 'd': diameter, 'enabled': True})
|
||||
holelist.append(
|
||||
{
|
||||
"featureName": candidateFaceName,
|
||||
"feature": f,
|
||||
"x": x,
|
||||
"y": y,
|
||||
"d": diameter,
|
||||
"enabled": True,
|
||||
}
|
||||
)
|
||||
features.append((baseobject, candidateFaceName))
|
||||
PathLog.debug("Found hole feature %s.%s" % (baseobject.Label, candidateFaceName))
|
||||
PathLog.debug(
|
||||
"Found hole feature %s.%s"
|
||||
% (baseobject.Label, candidateFaceName)
|
||||
)
|
||||
|
||||
PathLog.debug("holes found: {}".format(holelist))
|
||||
return features
|
||||
return features
|
||||
|
||||
@@ -31,13 +31,13 @@ from PySide import QtCore
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
ArchPanel = LazyLoader('ArchPanel', globals(), 'ArchPanel')
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
__doc__ = "Class and implementation of Path Engrave operation"
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
# PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
|
||||
# Qt translation handling
|
||||
@@ -46,27 +46,55 @@ def translate(context, text, disambig=None):
|
||||
|
||||
|
||||
class ObjectEngrave(PathEngraveBase.ObjectOp):
|
||||
'''Proxy class for Engrave operation.'''
|
||||
"""Proxy class for Engrave operation."""
|
||||
|
||||
def __init__(self, obj, name, parentJob):
|
||||
super(ObjectEngrave, self).__init__(obj, name, parentJob)
|
||||
self.wires = []
|
||||
|
||||
def opFeatures(self, obj):
|
||||
'''opFeatures(obj) ... return all standard features and edges based geomtries'''
|
||||
return PathOp.FeatureTool | PathOp.FeatureDepths | PathOp.FeatureHeights | PathOp.FeatureStepDown | PathOp.FeatureBaseEdges | PathOp.FeatureCoolant
|
||||
"""opFeatures(obj) ... return all standard features and edges based geomtries"""
|
||||
return (
|
||||
PathOp.FeatureTool
|
||||
| PathOp.FeatureDepths
|
||||
| PathOp.FeatureHeights
|
||||
| PathOp.FeatureStepDown
|
||||
| PathOp.FeatureBaseEdges
|
||||
| PathOp.FeatureCoolant
|
||||
)
|
||||
|
||||
def setupAdditionalProperties(self, obj):
|
||||
if not hasattr(obj, 'BaseShapes'):
|
||||
obj.addProperty("App::PropertyLinkList", "BaseShapes", "Path", QtCore.QT_TRANSLATE_NOOP("PathEngrave", "Additional base objects to be engraved"))
|
||||
obj.setEditorMode('BaseShapes', 2) # hide
|
||||
if not hasattr(obj, 'BaseObject'):
|
||||
obj.addProperty("App::PropertyLink", "BaseObject", "Path", QtCore.QT_TRANSLATE_NOOP("PathEngrave", "Additional base objects to be engraved"))
|
||||
obj.setEditorMode('BaseObject', 2) # hide
|
||||
if not hasattr(obj, "BaseShapes"):
|
||||
obj.addProperty(
|
||||
"App::PropertyLinkList",
|
||||
"BaseShapes",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathEngrave", "Additional base objects to be engraved"
|
||||
),
|
||||
)
|
||||
obj.setEditorMode("BaseShapes", 2) # hide
|
||||
if not hasattr(obj, "BaseObject"):
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"BaseObject",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathEngrave", "Additional base objects to be engraved"
|
||||
),
|
||||
)
|
||||
obj.setEditorMode("BaseObject", 2) # hide
|
||||
|
||||
def initOperation(self, obj):
|
||||
'''initOperation(obj) ... create engraving specific properties.'''
|
||||
obj.addProperty("App::PropertyInteger", "StartVertex", "Path", QtCore.QT_TRANSLATE_NOOP("PathEngrave", "The vertex index to start the path from"))
|
||||
"""initOperation(obj) ... create engraving specific properties."""
|
||||
obj.addProperty(
|
||||
"App::PropertyInteger",
|
||||
"StartVertex",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathEngrave", "The vertex index to start the path from"
|
||||
),
|
||||
)
|
||||
self.setupAdditionalProperties(obj)
|
||||
|
||||
def opOnDocumentRestored(self, obj):
|
||||
@@ -74,7 +102,7 @@ class ObjectEngrave(PathEngraveBase.ObjectOp):
|
||||
self.setupAdditionalProperties(obj)
|
||||
|
||||
def opExecute(self, obj):
|
||||
'''opExecute(obj) ... process engraving operation'''
|
||||
"""opExecute(obj) ... process engraving operation"""
|
||||
PathLog.track()
|
||||
|
||||
jobshapes = []
|
||||
@@ -106,36 +134,36 @@ class ObjectEngrave(PathEngraveBase.ObjectOp):
|
||||
PathLog.track(self.model)
|
||||
for base in self.model:
|
||||
PathLog.track(base.Label)
|
||||
if base.isDerivedFrom('Part::Part2DObject'):
|
||||
if base.isDerivedFrom("Part::Part2DObject"):
|
||||
jobshapes.append(base.Shape)
|
||||
elif base.isDerivedFrom('Sketcher::SketchObject'):
|
||||
elif base.isDerivedFrom("Sketcher::SketchObject"):
|
||||
jobshapes.append(base.Shape)
|
||||
elif hasattr(base, 'ArrayType'):
|
||||
elif hasattr(base, "ArrayType"):
|
||||
jobshapes.append(base.Shape)
|
||||
elif isinstance(base.Proxy, ArchPanel.PanelSheet):
|
||||
for tag in self.model[0].Proxy.getTags(self.model[0], transform=True):
|
||||
tagWires = []
|
||||
for w in tag.Wires:
|
||||
tagWires.append(Part.Wire(w.Edges))
|
||||
jobshapes.append(Part.makeCompound(tagWires))
|
||||
|
||||
if len(jobshapes) > 0:
|
||||
PathLog.debug('processing {} jobshapes'.format(len(jobshapes)))
|
||||
PathLog.debug("processing {} jobshapes".format(len(jobshapes)))
|
||||
wires = []
|
||||
for shape in jobshapes:
|
||||
shapeWires = shape.Wires
|
||||
PathLog.debug('jobshape has {} edges'.format(len(shape.Edges)))
|
||||
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
|
||||
PathLog.debug("jobshape has {} edges".format(len(shape.Edges)))
|
||||
self.commandlist.append(
|
||||
Path.Command(
|
||||
"G0", {"Z": obj.ClearanceHeight.Value, "F": self.vertRapid}
|
||||
)
|
||||
)
|
||||
self.buildpathocc(obj, shapeWires, self.getZValues(obj))
|
||||
wires.extend(shapeWires)
|
||||
self.wires = wires
|
||||
PathLog.debug('processing {} jobshapes -> {} wires'.format(len(jobshapes), len(wires)))
|
||||
PathLog.debug(
|
||||
"processing {} jobshapes -> {} wires".format(len(jobshapes), len(wires))
|
||||
)
|
||||
# the last command is a move to clearance, which is automatically added by PathOp
|
||||
if self.commandlist:
|
||||
self.commandlist.pop()
|
||||
|
||||
def opUpdateDepths(self, obj):
|
||||
'''updateDepths(obj) ... engraving is always done at the top most z-value'''
|
||||
"""updateDepths(obj) ... engraving is always done at the top most z-value"""
|
||||
job = PathUtils.findParentJob(obj)
|
||||
self.opSetDefaultValues(obj, job)
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ class ObjectHelix(PathCircularHoleBase.ObjectOp):
|
||||
|
||||
def circularHoleFeatures(self, obj):
|
||||
'''circularHoleFeatures(obj) ... enable features supported by Helix.'''
|
||||
return PathOp.FeatureStepDown | PathOp.FeatureBaseEdges | PathOp.FeatureBaseFaces | PathOp.FeatureBasePanels
|
||||
return PathOp.FeatureStepDown | PathOp.FeatureBaseEdges | PathOp.FeatureBaseFaces
|
||||
|
||||
def initCircularHoleOperation(self, obj):
|
||||
'''initCircularHoleOperation(obj) ... create helix specific properties.'''
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PathScripts.PathPostProcessor import PostProcessor
|
||||
from PySide import QtCore
|
||||
import FreeCAD
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
@@ -29,13 +31,11 @@ import PathScripts.PathToolController as PathToolController
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import json
|
||||
import time
|
||||
from PathScripts.PathPostProcessor import PostProcessor
|
||||
from PySide import QtCore
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
ArchPanel = LazyLoader('ArchPanel', globals(), 'ArchPanel')
|
||||
Draft = LazyLoader('Draft', globals(), 'Draft')
|
||||
|
||||
Draft = LazyLoader("Draft", globals(), "Draft")
|
||||
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
@@ -48,45 +48,41 @@ def translate(context, text, disambig=None):
|
||||
|
||||
class JobTemplate:
|
||||
# pylint: disable=no-init
|
||||
'''Attribute and sub element strings for template export/import.'''
|
||||
Description = 'Desc'
|
||||
GeometryTolerance = 'Tolerance'
|
||||
Job = 'Job'
|
||||
PostProcessor = 'Post'
|
||||
PostProcessorArgs = 'PostArgs'
|
||||
PostProcessorOutputFile = 'Output'
|
||||
Fixtures = 'Fixtures'
|
||||
OrderOutputBy = 'OrderOutputBy'
|
||||
SplitOutput = 'SplitOutput'
|
||||
SetupSheet = 'SetupSheet'
|
||||
Stock = 'Stock'
|
||||
"""Attribute and sub element strings for template export/import."""
|
||||
Description = "Desc"
|
||||
GeometryTolerance = "Tolerance"
|
||||
Job = "Job"
|
||||
PostProcessor = "Post"
|
||||
PostProcessorArgs = "PostArgs"
|
||||
PostProcessorOutputFile = "Output"
|
||||
Fixtures = "Fixtures"
|
||||
OrderOutputBy = "OrderOutputBy"
|
||||
SplitOutput = "SplitOutput"
|
||||
SetupSheet = "SetupSheet"
|
||||
Stock = "Stock"
|
||||
# TCs are grouped under Tools in a job, the template refers to them directly though
|
||||
ToolController = 'ToolController'
|
||||
Version = 'Version'
|
||||
|
||||
|
||||
def isArchPanelSheet(obj):
|
||||
return hasattr(obj, 'Proxy') and isinstance(obj.Proxy, ArchPanel.PanelSheet)
|
||||
ToolController = "ToolController"
|
||||
Version = "Version"
|
||||
|
||||
|
||||
def isResourceClone(obj, propLink, resourceName):
|
||||
# pylint: disable=unused-argument
|
||||
if hasattr(propLink, 'PathResource') and (resourceName is None or resourceName == propLink.PathResource):
|
||||
if hasattr(propLink, "PathResource") and (
|
||||
resourceName is None or resourceName == propLink.PathResource
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def createResourceClone(obj, orig, name, icon):
|
||||
if isArchPanelSheet(orig):
|
||||
# can't clone panel sheets - they have to be panel sheets
|
||||
return orig
|
||||
|
||||
clone = Draft.clone(orig)
|
||||
clone.Label = "%s-%s" % (name, orig.Label)
|
||||
clone.addProperty('App::PropertyString', 'PathResource')
|
||||
clone.addProperty("App::PropertyString", "PathResource")
|
||||
clone.PathResource = name
|
||||
if clone.ViewObject:
|
||||
import PathScripts.PathIconViewProvider
|
||||
|
||||
PathScripts.PathIconViewProvider.Attach(clone.ViewObject, icon)
|
||||
clone.ViewObject.Visibility = False
|
||||
clone.ViewObject.Transparency = 80
|
||||
@@ -95,7 +91,7 @@ def createResourceClone(obj, orig, name, icon):
|
||||
|
||||
|
||||
def createModelResourceClone(obj, orig):
|
||||
return createResourceClone(obj, orig, 'Model', 'BaseGeometry')
|
||||
return createResourceClone(obj, orig, "Model", "BaseGeometry")
|
||||
|
||||
|
||||
class NotificationClass(QtCore.QObject):
|
||||
@@ -106,30 +102,108 @@ Notification = NotificationClass()
|
||||
|
||||
|
||||
class ObjectJob:
|
||||
|
||||
def __init__(self, obj, models, templateFile=None):
|
||||
self.obj = obj
|
||||
obj.addProperty("App::PropertyFile", "PostProcessorOutputFile", "Output", QtCore.QT_TRANSLATE_NOOP("PathJob", "The NC output file for this project"))
|
||||
obj.addProperty("App::PropertyEnumeration", "PostProcessor", "Output", QtCore.QT_TRANSLATE_NOOP("PathJob", "Select the Post Processor"))
|
||||
obj.addProperty("App::PropertyString", "PostProcessorArgs", "Output", QtCore.QT_TRANSLATE_NOOP("PathJob", "Arguments for the Post Processor (specific to the script)"))
|
||||
obj.addProperty("App::PropertyString", "LastPostProcessDate", "Output", QtCore.QT_TRANSLATE_NOOP("PathJob", "Last Time the Job was post-processed"))
|
||||
obj.setEditorMode('LastPostProcessDate', 2) # Hide
|
||||
obj.addProperty("App::PropertyString", "LastPostProcessOutput", "Output", QtCore.QT_TRANSLATE_NOOP("PathJob", "Last Time the Job was post-processed"))
|
||||
obj.setEditorMode('LastPostProcessOutput', 2) # Hide
|
||||
obj.addProperty(
|
||||
"App::PropertyFile",
|
||||
"PostProcessorOutputFile",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "The NC output file for this project"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"PostProcessor",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "Select the Post Processor"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"PostProcessorArgs",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "Arguments for the Post Processor (specific to the script)"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"LastPostProcessDate",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "Last Time the Job was post-processed"),
|
||||
)
|
||||
obj.setEditorMode("LastPostProcessDate", 2) # Hide
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"LastPostProcessOutput",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "Last Time the Job was post-processed"),
|
||||
)
|
||||
obj.setEditorMode("LastPostProcessOutput", 2) # Hide
|
||||
|
||||
obj.addProperty("App::PropertyString", "Description", "Path", QtCore.QT_TRANSLATE_NOOP("PathJob", "An optional description for this job"))
|
||||
obj.addProperty("App::PropertyString", "CycleTime", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "Job Cycle Time Estimation"))
|
||||
obj.setEditorMode('CycleTime', 1) # read-only
|
||||
obj.addProperty("App::PropertyDistance", "GeometryTolerance", "Geometry", QtCore.QT_TRANSLATE_NOOP("PathJob", "For computing Paths; smaller increases accuracy, but slows down computation"))
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"Description",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "An optional description for this job"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"CycleTime",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Job Cycle Time Estimation"),
|
||||
)
|
||||
obj.setEditorMode("CycleTime", 1) # read-only
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"GeometryTolerance",
|
||||
"Geometry",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob",
|
||||
"For computing Paths; smaller increases accuracy, but slows down computation",
|
||||
),
|
||||
)
|
||||
|
||||
obj.addProperty("App::PropertyLink", "Stock", "Base", QtCore.QT_TRANSLATE_NOOP("PathJob", "Solid object to be used as stock."))
|
||||
obj.addProperty("App::PropertyLink", "Operations", "Base", QtCore.QT_TRANSLATE_NOOP("PathJob", "Compound path of all operations in the order they are processed."))
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"Stock",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathJob", "Solid object to be used as stock."),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"Operations",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob",
|
||||
"Compound path of all operations in the order they are processed.",
|
||||
),
|
||||
)
|
||||
|
||||
obj.addProperty("App::PropertyBool", "SplitOutput", "Output", QtCore.QT_TRANSLATE_NOOP("PathJob", "Split output into multiple gcode files"))
|
||||
obj.addProperty("App::PropertyEnumeration", "OrderOutputBy", "WCS", QtCore.QT_TRANSLATE_NOOP("PathJob", "If multiple WCS, order the output this way"))
|
||||
obj.addProperty("App::PropertyStringList", "Fixtures", "WCS", QtCore.QT_TRANSLATE_NOOP("PathJob", "The Work Coordinate Systems for the Job"))
|
||||
obj.OrderOutputBy = ['Fixture', 'Tool', 'Operation']
|
||||
obj.Fixtures = ['G54']
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"SplitOutput",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "Split output into multiple gcode files"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"OrderOutputBy",
|
||||
"WCS",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "If multiple WCS, order the output this way"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyStringList",
|
||||
"Fixtures",
|
||||
"WCS",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "The Work Coordinate Systems for the Job"
|
||||
),
|
||||
)
|
||||
obj.OrderOutputBy = ["Fixture", "Tool", "Operation"]
|
||||
obj.Fixtures = ["G54"]
|
||||
|
||||
obj.PostProcessorOutputFile = PathPreferences.defaultOutputFile()
|
||||
obj.PostProcessor = postProcessors = PathPreferences.allEnabledPostProcessors()
|
||||
@@ -142,14 +216,16 @@ class ObjectJob:
|
||||
obj.PostProcessorArgs = PathPreferences.defaultPostProcessorArgs()
|
||||
obj.GeometryTolerance = PathPreferences.defaultGeometryTolerance()
|
||||
|
||||
ops = FreeCAD.ActiveDocument.addObject("Path::FeatureCompoundPython", "Operations")
|
||||
ops = FreeCAD.ActiveDocument.addObject(
|
||||
"Path::FeatureCompoundPython", "Operations"
|
||||
)
|
||||
if ops.ViewObject:
|
||||
ops.ViewObject.Proxy = 0
|
||||
ops.ViewObject.Visibility = False
|
||||
|
||||
obj.Operations = ops
|
||||
obj.setEditorMode('Operations', 2) # hide
|
||||
obj.setEditorMode('Placement', 2)
|
||||
obj.setEditorMode("Operations", 2) # hide
|
||||
obj.setEditorMode("Placement", 2)
|
||||
|
||||
self.setupSetupSheet(obj)
|
||||
self.setupBaseModel(obj, models)
|
||||
@@ -171,41 +247,73 @@ class ObjectJob:
|
||||
obj.Stock.ViewObject.Visibility = False
|
||||
|
||||
def setupSetupSheet(self, obj):
|
||||
if not getattr(obj, 'SetupSheet', None):
|
||||
obj.addProperty('App::PropertyLink', 'SetupSheet', 'Base', QtCore.QT_TRANSLATE_NOOP('PathJob', 'SetupSheet holding the settings for this job'))
|
||||
if not getattr(obj, "SetupSheet", None):
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"SetupSheet",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "SetupSheet holding the settings for this job"
|
||||
),
|
||||
)
|
||||
obj.SetupSheet = PathSetupSheet.Create()
|
||||
if obj.SetupSheet.ViewObject:
|
||||
import PathScripts.PathIconViewProvider
|
||||
PathScripts.PathIconViewProvider.Attach(obj.SetupSheet.ViewObject, 'SetupSheet')
|
||||
|
||||
PathScripts.PathIconViewProvider.Attach(
|
||||
obj.SetupSheet.ViewObject, "SetupSheet"
|
||||
)
|
||||
self.setupSheet = obj.SetupSheet.Proxy
|
||||
|
||||
def setupBaseModel(self, obj, models=None):
|
||||
PathLog.track(obj.Label, models)
|
||||
if not hasattr(obj, 'Model'):
|
||||
obj.addProperty("App::PropertyLink", "Model", "Base", QtCore.QT_TRANSLATE_NOOP("PathJob", "The base objects for all operations"))
|
||||
model = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "Model")
|
||||
if not hasattr(obj, "Model"):
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"Model",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "The base objects for all operations"
|
||||
),
|
||||
)
|
||||
model = FreeCAD.ActiveDocument.addObject(
|
||||
"App::DocumentObjectGroup", "Model"
|
||||
)
|
||||
if model.ViewObject:
|
||||
model.ViewObject.Visibility = False
|
||||
if models:
|
||||
model.addObjects([createModelResourceClone(obj, base) for base in models])
|
||||
model.addObjects(
|
||||
[createModelResourceClone(obj, base) for base in models]
|
||||
)
|
||||
obj.Model = model
|
||||
|
||||
if hasattr(obj, 'Base'):
|
||||
PathLog.info("Converting Job.Base to new Job.Model for {}".format(obj.Label))
|
||||
if hasattr(obj, "Base"):
|
||||
PathLog.info(
|
||||
"Converting Job.Base to new Job.Model for {}".format(obj.Label)
|
||||
)
|
||||
obj.Model.addObject(obj.Base)
|
||||
obj.Base = None
|
||||
obj.removeProperty('Base')
|
||||
obj.removeProperty("Base")
|
||||
|
||||
def setupToolTable(self, obj):
|
||||
if not hasattr(obj, 'Tools'):
|
||||
obj.addProperty("App::PropertyLink", "Tools", "Base", QtCore.QT_TRANSLATE_NOOP("PathJob", "Collection of all tool controllers for the job"))
|
||||
toolTable = FreeCAD.ActiveDocument.addObject("App::DocumentObjectGroup", "Tools")
|
||||
toolTable.Label = 'Tools'
|
||||
if not hasattr(obj, "Tools"):
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"Tools",
|
||||
"Base",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "Collection of all tool controllers for the job"
|
||||
),
|
||||
)
|
||||
toolTable = FreeCAD.ActiveDocument.addObject(
|
||||
"App::DocumentObjectGroup", "Tools"
|
||||
)
|
||||
toolTable.Label = "Tools"
|
||||
if toolTable.ViewObject:
|
||||
toolTable.ViewObject.Visibility = False
|
||||
if hasattr(obj, 'ToolController'):
|
||||
if hasattr(obj, "ToolController"):
|
||||
toolTable.addObjects(obj.ToolController)
|
||||
obj.removeProperty('ToolController')
|
||||
obj.removeProperty("ToolController")
|
||||
obj.Tools = toolTable
|
||||
|
||||
def removeBase(self, obj, base, removeFromModel):
|
||||
@@ -219,16 +327,22 @@ class ObjectJob:
|
||||
return PathStock.shapeBoundBox(obj.Model.Group)
|
||||
|
||||
def onDelete(self, obj, arg2=None):
|
||||
'''Called by the view provider, there doesn't seem to be a callback on the obj itself.'''
|
||||
"""Called by the view provider, there doesn't seem to be a callback on the obj itself."""
|
||||
PathLog.track(obj.Label, arg2)
|
||||
doc = obj.Document
|
||||
|
||||
if getattr(obj, 'Operations', None):
|
||||
if getattr(obj, "Operations", None):
|
||||
# the first to tear down are the ops, they depend on other resources
|
||||
PathLog.debug('taking down ops: %s' % [o.Name for o in self.allOperations()])
|
||||
PathLog.debug(
|
||||
"taking down ops: %s" % [o.Name for o in self.allOperations()]
|
||||
)
|
||||
while obj.Operations.Group:
|
||||
op = obj.Operations.Group[0]
|
||||
if not op.ViewObject or not hasattr(op.ViewObject.Proxy, 'onDelete') or op.ViewObject.Proxy.onDelete(op.ViewObject, ()):
|
||||
if (
|
||||
not op.ViewObject
|
||||
or not hasattr(op.ViewObject.Proxy, "onDelete")
|
||||
or op.ViewObject.Proxy.onDelete(op.ViewObject, ())
|
||||
):
|
||||
PathUtil.clearExpressionEngine(op)
|
||||
doc.removeObject(op.Name)
|
||||
obj.Operations.Group = []
|
||||
@@ -236,14 +350,14 @@ class ObjectJob:
|
||||
obj.Operations = None
|
||||
|
||||
# stock could depend on Model, so delete it first
|
||||
if getattr(obj, 'Stock', None):
|
||||
PathLog.debug('taking down stock')
|
||||
if getattr(obj, "Stock", None):
|
||||
PathLog.debug("taking down stock")
|
||||
PathUtil.clearExpressionEngine(obj.Stock)
|
||||
doc.removeObject(obj.Stock.Name)
|
||||
obj.Stock = None
|
||||
|
||||
# base doesn't depend on anything inside job
|
||||
if getattr(obj, 'Model', None):
|
||||
if getattr(obj, "Model", None):
|
||||
for base in obj.Model.Group:
|
||||
PathLog.debug("taking down base %s" % base.Label)
|
||||
self.removeBase(obj, base, False)
|
||||
@@ -252,8 +366,8 @@ class ObjectJob:
|
||||
obj.Model = None
|
||||
|
||||
# Tool controllers might refer to either legacy tool or toolbit
|
||||
if getattr(obj, 'Tools', None):
|
||||
PathLog.debug('taking down tool controller')
|
||||
if getattr(obj, "Tools", None):
|
||||
PathLog.debug("taking down tool controller")
|
||||
for tc in obj.Tools.Group:
|
||||
if hasattr(tc.Tool, "Proxy"):
|
||||
PathUtil.clearExpressionEngine(tc.Tool)
|
||||
@@ -266,7 +380,7 @@ class ObjectJob:
|
||||
obj.Tools = None
|
||||
|
||||
# SetupSheet
|
||||
if getattr(obj, 'SetupSheet', None):
|
||||
if getattr(obj, "SetupSheet", None):
|
||||
PathUtil.clearExpressionEngine(obj.SetupSheet)
|
||||
doc.removeObject(obj.SetupSheet.Name)
|
||||
obj.SetupSheet = None
|
||||
@@ -274,13 +388,15 @@ class ObjectJob:
|
||||
return True
|
||||
|
||||
def fixupOperations(self, obj):
|
||||
if getattr(obj.Operations, 'ViewObject', None):
|
||||
if getattr(obj.Operations, "ViewObject", None):
|
||||
try:
|
||||
obj.Operations.ViewObject.DisplayMode
|
||||
except Exception: # pylint: disable=broad-except
|
||||
name = obj.Operations.Name
|
||||
label = obj.Operations.Label
|
||||
ops = FreeCAD.ActiveDocument.addObject("Path::FeatureCompoundPython", "Operations")
|
||||
ops = FreeCAD.ActiveDocument.addObject(
|
||||
"Path::FeatureCompoundPython", "Operations"
|
||||
)
|
||||
ops.ViewObject.Proxy = 0
|
||||
ops.Group = obj.Operations.Group
|
||||
obj.Operations.Group = []
|
||||
@@ -294,23 +410,49 @@ class ObjectJob:
|
||||
self.setupSetupSheet(obj)
|
||||
self.setupToolTable(obj)
|
||||
|
||||
obj.setEditorMode('Operations', 2) # hide
|
||||
obj.setEditorMode('Placement', 2)
|
||||
obj.setEditorMode("Operations", 2) # hide
|
||||
obj.setEditorMode("Placement", 2)
|
||||
|
||||
if not hasattr(obj, 'CycleTime'):
|
||||
obj.addProperty("App::PropertyString", "CycleTime", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "Operations Cycle Time Estimation"))
|
||||
obj.setEditorMode('CycleTime', 1) # read-only
|
||||
if not hasattr(obj, "CycleTime"):
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"CycleTime",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Operations Cycle Time Estimation"),
|
||||
)
|
||||
obj.setEditorMode("CycleTime", 1) # read-only
|
||||
|
||||
if not hasattr(obj, "Fixtures"):
|
||||
obj.addProperty("App::PropertyStringList", "Fixtures", "WCS", QtCore.QT_TRANSLATE_NOOP("PathJob", "The Work Coordinate Systems for the Job"))
|
||||
obj.Fixtures = ['G54']
|
||||
obj.addProperty(
|
||||
"App::PropertyStringList",
|
||||
"Fixtures",
|
||||
"WCS",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "The Work Coordinate Systems for the Job"
|
||||
),
|
||||
)
|
||||
obj.Fixtures = ["G54"]
|
||||
|
||||
if not hasattr(obj, "OrderOutputBy"):
|
||||
obj.addProperty("App::PropertyEnumeration", "OrderOutputBy", "WCS", QtCore.QT_TRANSLATE_NOOP("PathJob", "If multiple WCS, order the output this way"))
|
||||
obj.OrderOutputBy = ['Fixture', 'Tool', 'Operation']
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"OrderOutputBy",
|
||||
"WCS",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "If multiple WCS, order the output this way"
|
||||
),
|
||||
)
|
||||
obj.OrderOutputBy = ["Fixture", "Tool", "Operation"]
|
||||
|
||||
if not hasattr(obj, "SplitOutput"):
|
||||
obj.addProperty("App::PropertyBool", "SplitOutput", "Output", QtCore.QT_TRANSLATE_NOOP("PathJob", "Split output into multiple gcode files"))
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"SplitOutput",
|
||||
"Output",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathJob", "Split output into multiple gcode files"
|
||||
),
|
||||
)
|
||||
obj.SplitOutput = False
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
@@ -320,17 +462,17 @@ class ObjectJob:
|
||||
self.tooltipArgs = processor.tooltipArgs
|
||||
|
||||
def baseObject(self, obj, base):
|
||||
'''Return the base object, not its clone.'''
|
||||
if isResourceClone(obj, base, 'Model') or isResourceClone(obj, base, 'Base'):
|
||||
"""Return the base object, not its clone."""
|
||||
if isResourceClone(obj, base, "Model") or isResourceClone(obj, base, "Base"):
|
||||
return base.Objects[0]
|
||||
return base
|
||||
|
||||
def baseObjects(self, obj):
|
||||
'''Return the base objects, not their clones.'''
|
||||
"""Return the base objects, not their clones."""
|
||||
return [self.baseObject(obj, base) for base in obj.Model.Group]
|
||||
|
||||
def resourceClone(self, obj, base):
|
||||
'''resourceClone(obj, base) ... Return the resource clone for base if it exists.'''
|
||||
"""resourceClone(obj, base) ... Return the resource clone for base if it exists."""
|
||||
if isResourceClone(obj, base, None):
|
||||
return base
|
||||
for b in obj.Model.Group:
|
||||
@@ -339,11 +481,11 @@ class ObjectJob:
|
||||
return None
|
||||
|
||||
def setFromTemplateFile(self, obj, template):
|
||||
'''setFromTemplateFile(obj, template) ... extract the properties from the given template file and assign to receiver.
|
||||
This will also create any TCs stored in the template.'''
|
||||
"""setFromTemplateFile(obj, template) ... extract the properties from the given template file and assign to receiver.
|
||||
This will also create any TCs stored in the template."""
|
||||
tcs = []
|
||||
if template:
|
||||
with open(PathUtil.toUnicode(template), 'rb') as fp:
|
||||
with open(PathUtil.toUnicode(template), "rb") as fp:
|
||||
attrs = json.load(fp)
|
||||
|
||||
if attrs.get(JobTemplate.Version) and 1 == int(attrs[JobTemplate.Version]):
|
||||
@@ -352,15 +494,19 @@ class ObjectJob:
|
||||
self.setupSheet.setFromTemplate(attrs[JobTemplate.SetupSheet])
|
||||
|
||||
if attrs.get(JobTemplate.GeometryTolerance):
|
||||
obj.GeometryTolerance = float(attrs.get(JobTemplate.GeometryTolerance))
|
||||
obj.GeometryTolerance = float(
|
||||
attrs.get(JobTemplate.GeometryTolerance)
|
||||
)
|
||||
if attrs.get(JobTemplate.PostProcessor):
|
||||
obj.PostProcessor = attrs.get(JobTemplate.PostProcessor)
|
||||
if attrs.get(JobTemplate.PostProcessorArgs):
|
||||
obj.PostProcessorArgs = attrs.get(JobTemplate.PostProcessorArgs)
|
||||
else:
|
||||
obj.PostProcessorArgs = ''
|
||||
obj.PostProcessorArgs = ""
|
||||
if attrs.get(JobTemplate.PostProcessorOutputFile):
|
||||
obj.PostProcessorOutputFile = attrs.get(JobTemplate.PostProcessorOutputFile)
|
||||
obj.PostProcessorOutputFile = attrs.get(
|
||||
JobTemplate.PostProcessorOutputFile
|
||||
)
|
||||
if attrs.get(JobTemplate.Description):
|
||||
obj.Description = attrs.get(JobTemplate.Description)
|
||||
|
||||
@@ -368,10 +514,14 @@ class ObjectJob:
|
||||
for tc in attrs.get(JobTemplate.ToolController):
|
||||
tcs.append(PathToolController.FromTemplate(tc))
|
||||
if attrs.get(JobTemplate.Stock):
|
||||
obj.Stock = PathStock.CreateFromTemplate(obj, attrs.get(JobTemplate.Stock))
|
||||
obj.Stock = PathStock.CreateFromTemplate(
|
||||
obj, attrs.get(JobTemplate.Stock)
|
||||
)
|
||||
|
||||
if attrs.get(JobTemplate.Fixtures):
|
||||
obj.Fixtures = [x for y in attrs.get(JobTemplate.Fixtures) for x in y]
|
||||
obj.Fixtures = [
|
||||
x for y in attrs.get(JobTemplate.Fixtures) for x in y
|
||||
]
|
||||
|
||||
if attrs.get(JobTemplate.OrderOutputBy):
|
||||
obj.OrderOutputBy = attrs.get(JobTemplate.OrderOutputBy)
|
||||
@@ -382,12 +532,15 @@ class ObjectJob:
|
||||
PathLog.debug("setting tool controllers (%d)" % len(tcs))
|
||||
obj.Tools.Group = tcs
|
||||
else:
|
||||
PathLog.error(translate('PathJob', "Unsupported PathJob template version %s") % attrs.get(JobTemplate.Version))
|
||||
PathLog.error(
|
||||
translate("PathJob", "Unsupported PathJob template version %s")
|
||||
% attrs.get(JobTemplate.Version)
|
||||
)
|
||||
if not tcs:
|
||||
self.addToolController(PathToolController.Create())
|
||||
|
||||
def templateAttrs(self, obj):
|
||||
'''templateAttrs(obj) ... answer a dictionary with all properties of the receiver that should be stored in a template file.'''
|
||||
"""templateAttrs(obj) ... answer a dictionary with all properties of the receiver that should be stored in a template file."""
|
||||
attrs = {}
|
||||
attrs[JobTemplate.Version] = 1
|
||||
if obj.PostProcessor:
|
||||
@@ -408,13 +561,13 @@ class ObjectJob:
|
||||
|
||||
def __setstate__(self, state):
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
if hasattr(obj, 'Proxy') and obj.Proxy == self:
|
||||
if hasattr(obj, "Proxy") and obj.Proxy == self:
|
||||
self.obj = obj
|
||||
break
|
||||
return None
|
||||
|
||||
def execute(self, obj):
|
||||
if getattr(obj, 'Operations', None):
|
||||
if getattr(obj, "Operations", None):
|
||||
obj.Path = obj.Operations.Path
|
||||
self.getCycleTime()
|
||||
|
||||
@@ -425,18 +578,23 @@ class ObjectJob:
|
||||
for op in self.obj.Operations.Group:
|
||||
|
||||
# Skip inactive operations
|
||||
if PathUtil.opProperty(op, 'Active') is False:
|
||||
if PathUtil.opProperty(op, "Active") is False:
|
||||
continue
|
||||
|
||||
# Skip operations that don't have a cycletime attribute
|
||||
if PathUtil.opProperty(op, 'CycleTime') is None:
|
||||
if PathUtil.opProperty(op, "CycleTime") is None:
|
||||
continue
|
||||
|
||||
formattedCycleTime = PathUtil.opProperty(op, 'CycleTime')
|
||||
formattedCycleTime = PathUtil.opProperty(op, "CycleTime")
|
||||
opCycleTime = 0
|
||||
try:
|
||||
# Convert the formatted time from HH:MM:SS to just seconds
|
||||
opCycleTime = sum(x * int(t) for x, t in zip([1, 60, 3600], reversed(formattedCycleTime.split(":"))))
|
||||
opCycleTime = sum(
|
||||
x * int(t)
|
||||
for x, t in zip(
|
||||
[1, 60, 3600], reversed(formattedCycleTime.split(":"))
|
||||
)
|
||||
)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
@@ -469,10 +627,26 @@ class ObjectJob:
|
||||
|
||||
def addToolController(self, tc):
|
||||
group = self.obj.Tools.Group
|
||||
PathLog.debug("addToolController(%s): %s" % (tc.Label, [t.Label for t in group]))
|
||||
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.Template.VertRapid))
|
||||
tc.setExpression('HorizRapid', "%s.%s" % (self.setupSheet.expressionReference(), PathSetupSheet.Template.HorizRapid))
|
||||
tc.setExpression(
|
||||
"VertRapid",
|
||||
"%s.%s"
|
||||
% (
|
||||
self.setupSheet.expressionReference(),
|
||||
PathSetupSheet.Template.VertRapid,
|
||||
),
|
||||
)
|
||||
tc.setExpression(
|
||||
"HorizRapid",
|
||||
"%s.%s"
|
||||
% (
|
||||
self.setupSheet.expressionReference(),
|
||||
PathSetupSheet.Template.HorizRapid,
|
||||
),
|
||||
)
|
||||
self.obj.Tools.addObject(tc)
|
||||
Notification.updateTC.emit(self.obj, tc)
|
||||
|
||||
@@ -480,17 +654,19 @@ class ObjectJob:
|
||||
ops = []
|
||||
|
||||
def collectBaseOps(op):
|
||||
if hasattr(op, 'TypeId'):
|
||||
if op.TypeId == 'Path::FeaturePython':
|
||||
if hasattr(op, "TypeId"):
|
||||
if op.TypeId == "Path::FeaturePython":
|
||||
ops.append(op)
|
||||
if hasattr(op, 'Base'):
|
||||
if hasattr(op, "Base"):
|
||||
collectBaseOps(op.Base)
|
||||
if op.TypeId == 'Path::FeatureCompoundPython':
|
||||
if op.TypeId == "Path::FeatureCompoundPython":
|
||||
ops.append(op)
|
||||
for sub in op.Group:
|
||||
collectBaseOps(sub)
|
||||
|
||||
if getattr(self.obj, 'Operations', None) and getattr(self.obj.Operations, 'Group', None):
|
||||
if getattr(self.obj, "Operations", None) and getattr(
|
||||
self.obj.Operations, "Group", None
|
||||
):
|
||||
for op in self.obj.Operations.Group:
|
||||
collectBaseOps(op)
|
||||
|
||||
@@ -505,25 +681,32 @@ class ObjectJob:
|
||||
|
||||
@classmethod
|
||||
def baseCandidates(cls):
|
||||
'''Answer all objects in the current document which could serve as a Base for a job.'''
|
||||
return sorted([obj for obj in FreeCAD.ActiveDocument.Objects if cls.isBaseCandidate(obj)], key=lambda o: o.Label)
|
||||
"""Answer all objects in the current document which could serve as a Base for a job."""
|
||||
return sorted(
|
||||
[obj for obj in FreeCAD.ActiveDocument.Objects if cls.isBaseCandidate(obj)],
|
||||
key=lambda o: o.Label,
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def isBaseCandidate(cls, obj):
|
||||
'''Answer true if the given object can be used as a Base for a job.'''
|
||||
return PathUtil.isValidBaseObject(obj) or isArchPanelSheet(obj)
|
||||
"""Answer true if the given object can be used as a Base for a job."""
|
||||
return PathUtil.isValidBaseObject(obj)
|
||||
|
||||
|
||||
def Instances():
|
||||
'''Instances() ... Return all Jobs in the current active document.'''
|
||||
"""Instances() ... Return all Jobs in the current active document."""
|
||||
if FreeCAD.ActiveDocument:
|
||||
return [job for job in FreeCAD.ActiveDocument.Objects if hasattr(job, 'Proxy') and isinstance(job.Proxy, ObjectJob)]
|
||||
return [
|
||||
job
|
||||
for job in FreeCAD.ActiveDocument.Objects
|
||||
if hasattr(job, "Proxy") and isinstance(job.Proxy, ObjectJob)
|
||||
]
|
||||
return []
|
||||
|
||||
|
||||
def Create(name, base, templateFile=None):
|
||||
'''Create(name, base, templateFile=None) ... creates a new job and all it's resources.
|
||||
If a template file is specified the new job is initialized with the values from the template.'''
|
||||
"""Create(name, base, templateFile=None) ... creates a new job and all it's resources.
|
||||
If a template file is specified the new job is initialized with the values from the template."""
|
||||
if isinstance(base[0], str):
|
||||
models = []
|
||||
for baseName in base:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -24,17 +24,18 @@ import time
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
from PathScripts.PathUtils import waiting_effects
|
||||
import Path
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
from PathScripts.PathUtils import waiting_effects
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
Part = LazyLoader('Part', globals(), 'Part')
|
||||
|
||||
Part = LazyLoader("Part", globals(), "Part")
|
||||
|
||||
__title__ = "Base class for all operations."
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
@@ -65,11 +66,11 @@ FeatureLocations = 0x1000 # Locations
|
||||
FeatureCoolant = 0x2000 # Coolant
|
||||
FeatureDiameters = 0x4000 # Turning Diameters
|
||||
|
||||
FeatureBaseGeometry = FeatureBaseVertexes | FeatureBaseFaces | FeatureBaseEdges | FeatureBasePanels
|
||||
FeatureBaseGeometry = FeatureBaseVertexes | FeatureBaseFaces | FeatureBaseEdges
|
||||
|
||||
|
||||
class ObjectOp(object):
|
||||
'''
|
||||
"""
|
||||
Base class for proxy objects of all Path operations.
|
||||
|
||||
Use this class as a base class for new operations. It provides properties
|
||||
@@ -88,7 +89,6 @@ class ObjectOp(object):
|
||||
FeatureBaseVertexes ... Base geometry support for vertexes
|
||||
FeatureBaseEdges ... Base geometry support for edges
|
||||
FeatureBaseFaces ... Base geometry support for faces
|
||||
FeatureBasePanels ... Base geometry support for Arch.Panels
|
||||
FeatureLocations ... Base location support
|
||||
FeatureCoolant ... Support for operation coolant
|
||||
FeatureDiameters ... Support for turning operation diameters
|
||||
@@ -98,35 +98,93 @@ class ObjectOp(object):
|
||||
but implement the function opOnChanged().
|
||||
If a base class overwrites a base API function it should call the super's
|
||||
implementation - otherwise the base functionality might be broken.
|
||||
'''
|
||||
"""
|
||||
|
||||
def addBaseProperty(self, obj):
|
||||
obj.addProperty("App::PropertyLinkSubListGlobal", "Base", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "The base geometry for this operation"))
|
||||
obj.addProperty(
|
||||
"App::PropertyLinkSubListGlobal",
|
||||
"Base",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "The base geometry for this operation"),
|
||||
)
|
||||
|
||||
def addOpValues(self, obj, values):
|
||||
if 'start' in values:
|
||||
obj.addProperty("App::PropertyDistance", "OpStartDepth", "Op Values", QtCore.QT_TRANSLATE_NOOP("PathOp", "Holds the calculated value for the StartDepth"))
|
||||
obj.setEditorMode('OpStartDepth', 1) # read-only
|
||||
if 'final' in values:
|
||||
obj.addProperty("App::PropertyDistance", "OpFinalDepth", "Op Values", QtCore.QT_TRANSLATE_NOOP("PathOp", "Holds the calculated value for the FinalDepth"))
|
||||
obj.setEditorMode('OpFinalDepth', 1) # read-only
|
||||
if 'tooldia' in values:
|
||||
obj.addProperty("App::PropertyDistance", "OpToolDiameter", "Op Values", QtCore.QT_TRANSLATE_NOOP("PathOp", "Holds the diameter of the tool"))
|
||||
obj.setEditorMode('OpToolDiameter', 1) # read-only
|
||||
if 'stockz' in values:
|
||||
obj.addProperty("App::PropertyDistance", "OpStockZMax", "Op Values", QtCore.QT_TRANSLATE_NOOP("PathOp", "Holds the max Z value of Stock"))
|
||||
obj.setEditorMode('OpStockZMax', 1) # read-only
|
||||
obj.addProperty("App::PropertyDistance", "OpStockZMin", "Op Values", QtCore.QT_TRANSLATE_NOOP("PathOp", "Holds the min Z value of Stock"))
|
||||
obj.setEditorMode('OpStockZMin', 1) # read-only
|
||||
if "start" in values:
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"OpStartDepth",
|
||||
"Op Values",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Holds the calculated value for the StartDepth"
|
||||
),
|
||||
)
|
||||
obj.setEditorMode("OpStartDepth", 1) # read-only
|
||||
if "final" in values:
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"OpFinalDepth",
|
||||
"Op Values",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Holds the calculated value for the FinalDepth"
|
||||
),
|
||||
)
|
||||
obj.setEditorMode("OpFinalDepth", 1) # read-only
|
||||
if "tooldia" in values:
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"OpToolDiameter",
|
||||
"Op Values",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Holds the diameter of the tool"),
|
||||
)
|
||||
obj.setEditorMode("OpToolDiameter", 1) # read-only
|
||||
if "stockz" in values:
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"OpStockZMax",
|
||||
"Op Values",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Holds the max Z value of Stock"),
|
||||
)
|
||||
obj.setEditorMode("OpStockZMax", 1) # read-only
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"OpStockZMin",
|
||||
"Op Values",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Holds the min Z value of Stock"),
|
||||
)
|
||||
obj.setEditorMode("OpStockZMin", 1) # read-only
|
||||
|
||||
def __init__(self, obj, name, parentJob=None):
|
||||
PathLog.track()
|
||||
|
||||
obj.addProperty("App::PropertyBool", "Active", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "Make False, to prevent operation from generating code"))
|
||||
obj.addProperty("App::PropertyString", "Comment", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "An optional comment for this Operation"))
|
||||
obj.addProperty("App::PropertyString", "UserLabel", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "User Assigned Label"))
|
||||
obj.addProperty("App::PropertyString", "CycleTime", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "Operations Cycle Time Estimation"))
|
||||
obj.setEditorMode('CycleTime', 1) # read-only
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"Active",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Make False, to prevent operation from generating code"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"Comment",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "An optional comment for this Operation"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"UserLabel",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "User Assigned Label"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"CycleTime",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Operations Cycle Time Estimation"),
|
||||
)
|
||||
obj.setEditorMode("CycleTime", 1) # read-only
|
||||
|
||||
features = self.opFeatures(obj)
|
||||
|
||||
@@ -134,45 +192,136 @@ class ObjectOp(object):
|
||||
self.addBaseProperty(obj)
|
||||
|
||||
if FeatureLocations & features:
|
||||
obj.addProperty("App::PropertyVectorList", "Locations", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "Base locations for this operation"))
|
||||
obj.addProperty(
|
||||
"App::PropertyVectorList",
|
||||
"Locations",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Base locations for this operation"),
|
||||
)
|
||||
|
||||
if FeatureTool & features:
|
||||
obj.addProperty("App::PropertyLink", "ToolController", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "The tool controller that will be used to calculate the path"))
|
||||
self.addOpValues(obj, ['tooldia'])
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"ToolController",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp",
|
||||
"The tool controller that will be used to calculate the path",
|
||||
),
|
||||
)
|
||||
self.addOpValues(obj, ["tooldia"])
|
||||
|
||||
if FeatureCoolant & features:
|
||||
obj.addProperty("App::PropertyString", "CoolantMode", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "Coolant mode for this operation"))
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"CoolantMode",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Coolant mode for this operation"),
|
||||
)
|
||||
|
||||
if FeatureDepths & features:
|
||||
obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("PathOp", "Starting Depth of Tool- first cut depth in Z"))
|
||||
obj.addProperty("App::PropertyDistance", "FinalDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("PathOp", "Final Depth of Tool- lowest value in Z"))
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"StartDepth",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Starting Depth of Tool- first cut depth in Z"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"FinalDepth",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Final Depth of Tool- lowest value in Z"
|
||||
),
|
||||
)
|
||||
if FeatureNoFinalDepth & features:
|
||||
obj.setEditorMode('FinalDepth', 2) # hide
|
||||
self.addOpValues(obj, ['start', 'final'])
|
||||
obj.setEditorMode("FinalDepth", 2) # hide
|
||||
self.addOpValues(obj, ["start", "final"])
|
||||
else:
|
||||
# StartDepth has become necessary for expressions on other properties
|
||||
obj.addProperty("App::PropertyDistance", "StartDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("PathOp", "Starting Depth internal use only for derived values"))
|
||||
obj.setEditorMode('StartDepth', 1) # read-only
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"StartDepth",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Starting Depth internal use only for derived values"
|
||||
),
|
||||
)
|
||||
obj.setEditorMode("StartDepth", 1) # read-only
|
||||
|
||||
self.addOpValues(obj, ['stockz'])
|
||||
self.addOpValues(obj, ["stockz"])
|
||||
|
||||
if FeatureStepDown & features:
|
||||
obj.addProperty("App::PropertyDistance", "StepDown", "Depth", QtCore.QT_TRANSLATE_NOOP("PathOp", "Incremental Step Down of Tool"))
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"StepDown",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Incremental Step Down of Tool"),
|
||||
)
|
||||
|
||||
if FeatureFinishDepth & features:
|
||||
obj.addProperty("App::PropertyDistance", "FinishDepth", "Depth", QtCore.QT_TRANSLATE_NOOP("PathOp", "Maximum material removed on final pass."))
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"FinishDepth",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Maximum material removed on final pass."
|
||||
),
|
||||
)
|
||||
|
||||
if FeatureHeights & features:
|
||||
obj.addProperty("App::PropertyDistance", "ClearanceHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("PathOp", "The height needed to clear clamps and obstructions"))
|
||||
obj.addProperty("App::PropertyDistance", "SafeHeight", "Depth", QtCore.QT_TRANSLATE_NOOP("PathOp", "Rapid Safety Height between locations."))
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"ClearanceHeight",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "The height needed to clear clamps and obstructions"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"SafeHeight",
|
||||
"Depth",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Rapid Safety Height between locations."
|
||||
),
|
||||
)
|
||||
|
||||
if FeatureStartPoint & features:
|
||||
obj.addProperty("App::PropertyVectorDistance", "StartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("PathOp", "The start point of this path"))
|
||||
obj.addProperty("App::PropertyBool", "UseStartPoint", "Start Point", QtCore.QT_TRANSLATE_NOOP("PathOp", "Make True, if specifying a Start Point"))
|
||||
obj.addProperty(
|
||||
"App::PropertyVectorDistance",
|
||||
"StartPoint",
|
||||
"Start Point",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "The start point of this path"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"UseStartPoint",
|
||||
"Start Point",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Make True, if specifying a Start Point"
|
||||
),
|
||||
)
|
||||
|
||||
if FeatureDiameters & features:
|
||||
obj.addProperty("App::PropertyDistance", "MinDiameter", "Diameter", QtCore.QT_TRANSLATE_NOOP("PathOp", "Lower limit of the turning diameter"))
|
||||
obj.addProperty("App::PropertyDistance", "MaxDiameter", "Diameter", QtCore.QT_TRANSLATE_NOOP("PathOp", "Upper limit of the turning diameter."))
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"MinDiameter",
|
||||
"Diameter",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Lower limit of the turning diameter"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyDistance",
|
||||
"MaxDiameter",
|
||||
"Diameter",
|
||||
QtCore.QT_TRANSLATE_NOOP(
|
||||
"PathOp", "Upper limit of the turning diameter."
|
||||
),
|
||||
)
|
||||
|
||||
# members being set later
|
||||
self.commandlist = None
|
||||
@@ -199,135 +348,153 @@ class ObjectOp(object):
|
||||
obj.Proxy = self
|
||||
|
||||
def setEditorModes(self, obj, features):
|
||||
'''Editor modes are not preserved during document store/restore, set editor modes for all properties'''
|
||||
"""Editor modes are not preserved during document store/restore, set editor modes for all properties"""
|
||||
|
||||
for op in ['OpStartDepth', 'OpFinalDepth', 'OpToolDiameter', 'CycleTime']:
|
||||
for op in ["OpStartDepth", "OpFinalDepth", "OpToolDiameter", "CycleTime"]:
|
||||
if hasattr(obj, op):
|
||||
obj.setEditorMode(op, 1) # read-only
|
||||
|
||||
if FeatureDepths & features:
|
||||
if FeatureNoFinalDepth & features:
|
||||
obj.setEditorMode('OpFinalDepth', 2)
|
||||
obj.setEditorMode("OpFinalDepth", 2)
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
features = self.opFeatures(obj)
|
||||
if FeatureBaseGeometry & features and 'App::PropertyLinkSubList' == obj.getTypeIdOfProperty('Base'):
|
||||
if (
|
||||
FeatureBaseGeometry & features
|
||||
and "App::PropertyLinkSubList" == obj.getTypeIdOfProperty("Base")
|
||||
):
|
||||
PathLog.info("Replacing link property with global link (%s)." % obj.State)
|
||||
base = obj.Base
|
||||
obj.removeProperty('Base')
|
||||
obj.removeProperty("Base")
|
||||
self.addBaseProperty(obj)
|
||||
obj.Base = base
|
||||
obj.touch()
|
||||
obj.Document.recompute()
|
||||
|
||||
if FeatureTool & features and not hasattr(obj, 'OpToolDiameter'):
|
||||
self.addOpValues(obj, ['tooldia'])
|
||||
if FeatureTool & features and not hasattr(obj, "OpToolDiameter"):
|
||||
self.addOpValues(obj, ["tooldia"])
|
||||
|
||||
if FeatureCoolant & features and not hasattr(obj, 'CoolantMode'):
|
||||
obj.addProperty("App::PropertyString", "CoolantMode", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "Coolant option for this operation"))
|
||||
if FeatureCoolant & features and not hasattr(obj, "CoolantMode"):
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"CoolantMode",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Coolant option for this operation"),
|
||||
)
|
||||
|
||||
if FeatureDepths & features and not hasattr(obj, 'OpStartDepth'):
|
||||
self.addOpValues(obj, ['start', 'final'])
|
||||
if FeatureDepths & features and not hasattr(obj, "OpStartDepth"):
|
||||
self.addOpValues(obj, ["start", "final"])
|
||||
if FeatureNoFinalDepth & features:
|
||||
obj.setEditorMode('OpFinalDepth', 2)
|
||||
obj.setEditorMode("OpFinalDepth", 2)
|
||||
|
||||
if not hasattr(obj, 'OpStockZMax'):
|
||||
self.addOpValues(obj, ['stockz'])
|
||||
if not hasattr(obj, "OpStockZMax"):
|
||||
self.addOpValues(obj, ["stockz"])
|
||||
|
||||
if not hasattr(obj, 'CycleTime'):
|
||||
obj.addProperty("App::PropertyString", "CycleTime", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "Operations Cycle Time Estimation"))
|
||||
if not hasattr(obj, "CycleTime"):
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"CycleTime",
|
||||
"Path",
|
||||
QtCore.QT_TRANSLATE_NOOP("PathOp", "Operations Cycle Time Estimation"),
|
||||
)
|
||||
|
||||
self.setEditorModes(obj, features)
|
||||
self.opOnDocumentRestored(obj)
|
||||
|
||||
def __getstate__(self):
|
||||
'''__getstat__(self) ... called when receiver is saved.
|
||||
Can safely be overwritten by subclasses.'''
|
||||
"""__getstat__(self) ... called when receiver is saved.
|
||||
Can safely be overwritten by subclasses."""
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
'''__getstat__(self) ... called when receiver is restored.
|
||||
Can safely be overwritten by subclasses.'''
|
||||
"""__getstat__(self) ... called when receiver is restored.
|
||||
Can safely be overwritten by subclasses."""
|
||||
return None
|
||||
|
||||
def opFeatures(self, obj):
|
||||
'''opFeatures(obj) ... returns the OR'ed list of features used and supported by the operation.
|
||||
"""opFeatures(obj) ... returns the OR'ed list of features used and supported by the operation.
|
||||
The default implementation returns "FeatureTool | FeatureDepths | FeatureHeights | FeatureStartPoint"
|
||||
Should be overwritten by subclasses.'''
|
||||
Should be overwritten by subclasses."""
|
||||
# pylint: disable=unused-argument
|
||||
return FeatureTool | FeatureDepths | FeatureHeights | FeatureStartPoint | FeatureBaseGeometry | FeatureFinishDepth | FeatureCoolant
|
||||
return (
|
||||
FeatureTool
|
||||
| FeatureDepths
|
||||
| FeatureHeights
|
||||
| FeatureStartPoint
|
||||
| FeatureBaseGeometry
|
||||
| FeatureFinishDepth
|
||||
| FeatureCoolant
|
||||
)
|
||||
|
||||
def initOperation(self, obj):
|
||||
'''initOperation(obj) ... implement to create additional properties.
|
||||
Should be overwritten by subclasses.'''
|
||||
"""initOperation(obj) ... implement to create additional properties.
|
||||
Should be overwritten by subclasses."""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def opOnDocumentRestored(self, obj):
|
||||
'''opOnDocumentRestored(obj) ... implement if an op needs special handling like migrating the data model.
|
||||
Should be overwritten by subclasses.'''
|
||||
"""opOnDocumentRestored(obj) ... implement if an op needs special handling like migrating the data model.
|
||||
Should be overwritten by subclasses."""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def opOnChanged(self, obj, prop):
|
||||
'''opOnChanged(obj, prop) ... overwrite to process property changes.
|
||||
"""opOnChanged(obj, prop) ... overwrite to process property changes.
|
||||
This is a callback function that is invoked each time a property of the
|
||||
receiver is assigned a value. Note that the FC framework does not
|
||||
distinguish between assigning a different value and assigning the same
|
||||
value again.
|
||||
Can safely be overwritten by subclasses.'''
|
||||
Can safely be overwritten by subclasses."""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def opSetDefaultValues(self, obj, job):
|
||||
'''opSetDefaultValues(obj, job) ... overwrite to set initial default values.
|
||||
"""opSetDefaultValues(obj, job) ... overwrite to set initial default values.
|
||||
Called after the receiver has been fully created with all properties.
|
||||
Can safely be overwritten by subclasses.'''
|
||||
Can safely be overwritten by subclasses."""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def opUpdateDepths(self, obj):
|
||||
'''opUpdateDepths(obj) ... overwrite to implement special depths calculation.
|
||||
Can safely be overwritten by subclass.'''
|
||||
"""opUpdateDepths(obj) ... overwrite to implement special depths calculation.
|
||||
Can safely be overwritten by subclass."""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def opExecute(self, obj):
|
||||
'''opExecute(obj) ... called whenever the receiver needs to be recalculated.
|
||||
"""opExecute(obj) ... called whenever the receiver needs to be recalculated.
|
||||
See documentation of execute() for a list of base functionality provided.
|
||||
Should be overwritten by subclasses.'''
|
||||
Should be overwritten by subclasses."""
|
||||
pass # pylint: disable=unnecessary-pass
|
||||
|
||||
def opRejectAddBase(self, obj, base, sub):
|
||||
'''opRejectAddBase(base, sub) ... if op returns True the addition of the feature is prevented.
|
||||
Should be overwritten by subclasses.'''
|
||||
"""opRejectAddBase(base, sub) ... if op returns True the addition of the feature is prevented.
|
||||
Should be overwritten by subclasses."""
|
||||
# pylint: disable=unused-argument
|
||||
return False
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
'''onChanged(obj, prop) ... base implementation of the FC notification framework.
|
||||
Do not overwrite, overwrite opOnChanged() instead.'''
|
||||
"""onChanged(obj, prop) ... base implementation of the FC notification framework.
|
||||
Do not overwrite, overwrite opOnChanged() instead."""
|
||||
|
||||
# there's a bit of cycle going on here, if sanitizeBase causes the transaction to
|
||||
# be cancelled we end right here again with the unsainitized Base - if that is the
|
||||
# case, stop the cycle and return immediately
|
||||
if prop == 'Base' and self.sanitizeBase(obj):
|
||||
if prop == "Base" and self.sanitizeBase(obj):
|
||||
return
|
||||
|
||||
if 'Restore' not in obj.State and prop in ['Base', 'StartDepth', 'FinalDepth']:
|
||||
if "Restore" not in obj.State and prop in ["Base", "StartDepth", "FinalDepth"]:
|
||||
self.updateDepths(obj, True)
|
||||
|
||||
self.opOnChanged(obj, prop)
|
||||
|
||||
def applyExpression(self, obj, prop, expr):
|
||||
'''applyExpression(obj, prop, expr) ... set expression expr on obj.prop if expr is set'''
|
||||
"""applyExpression(obj, prop, expr) ... set expression expr on obj.prop if expr is set"""
|
||||
if expr:
|
||||
obj.setExpression(prop, expr)
|
||||
return True
|
||||
return False
|
||||
|
||||
def setDefaultValues(self, obj):
|
||||
'''setDefaultValues(obj) ... base implementation.
|
||||
Do not overwrite, overwrite opSetDefaultValues() instead.'''
|
||||
if self.job:
|
||||
job = self.job
|
||||
else:
|
||||
job = PathUtils.addToJob(obj)
|
||||
"""setDefaultValues(obj) ... base implementation.
|
||||
Do not overwrite, overwrite opSetDefaultValues() instead."""
|
||||
job = PathUtils.addToJob(obj)
|
||||
|
||||
obj.Active = True
|
||||
|
||||
@@ -335,7 +502,9 @@ class ObjectOp(object):
|
||||
|
||||
if FeatureTool & features:
|
||||
if 1 < len(job.Operations.Group):
|
||||
obj.ToolController = PathUtil.toolControllerForOp(job.Operations.Group[-2])
|
||||
obj.ToolController = PathUtil.toolControllerForOp(
|
||||
job.Operations.Group[-2]
|
||||
)
|
||||
else:
|
||||
obj.ToolController = PathUtils.findToolController(obj, self)
|
||||
if not obj.ToolController:
|
||||
@@ -346,11 +515,15 @@ class ObjectOp(object):
|
||||
obj.CoolantMode = job.SetupSheet.CoolantMode
|
||||
|
||||
if FeatureDepths & features:
|
||||
if self.applyExpression(obj, 'StartDepth', job.SetupSheet.StartDepthExpression):
|
||||
if self.applyExpression(
|
||||
obj, "StartDepth", job.SetupSheet.StartDepthExpression
|
||||
):
|
||||
obj.OpStartDepth = 1.0
|
||||
else:
|
||||
obj.StartDepth = 1.0
|
||||
if self.applyExpression(obj, 'FinalDepth', job.SetupSheet.FinalDepthExpression):
|
||||
if self.applyExpression(
|
||||
obj, "FinalDepth", job.SetupSheet.FinalDepthExpression
|
||||
):
|
||||
obj.OpFinalDepth = 0.0
|
||||
else:
|
||||
obj.FinalDepth = 0.0
|
||||
@@ -358,20 +531,26 @@ class ObjectOp(object):
|
||||
obj.StartDepth = 1.0
|
||||
|
||||
if FeatureStepDown & features:
|
||||
if not self.applyExpression(obj, 'StepDown', job.SetupSheet.StepDownExpression):
|
||||
obj.StepDown = '1 mm'
|
||||
if not self.applyExpression(
|
||||
obj, "StepDown", job.SetupSheet.StepDownExpression
|
||||
):
|
||||
obj.StepDown = "1 mm"
|
||||
|
||||
if FeatureHeights & features:
|
||||
if job.SetupSheet.SafeHeightExpression:
|
||||
if not self.applyExpression(obj, 'SafeHeight', job.SetupSheet.SafeHeightExpression):
|
||||
obj.SafeHeight = '3 mm'
|
||||
if not self.applyExpression(
|
||||
obj, "SafeHeight", job.SetupSheet.SafeHeightExpression
|
||||
):
|
||||
obj.SafeHeight = "3 mm"
|
||||
if job.SetupSheet.ClearanceHeightExpression:
|
||||
if not self.applyExpression(obj, 'ClearanceHeight', job.SetupSheet.ClearanceHeightExpression):
|
||||
obj.ClearanceHeight = '5 mm'
|
||||
if not self.applyExpression(
|
||||
obj, "ClearanceHeight", job.SetupSheet.ClearanceHeightExpression
|
||||
):
|
||||
obj.ClearanceHeight = "5 mm"
|
||||
|
||||
if FeatureDiameters & features:
|
||||
obj.MinDiameter = '0 mm'
|
||||
obj.MaxDiameter = '0 mm'
|
||||
obj.MinDiameter = "0 mm"
|
||||
obj.MaxDiameter = "0 mm"
|
||||
if job.Stock:
|
||||
obj.MaxDiameter = job.Stock.Shape.BoundBox.XLength
|
||||
|
||||
@@ -389,7 +568,10 @@ class ObjectOp(object):
|
||||
return False
|
||||
if not job.Model.Group:
|
||||
if not ignoreErrors:
|
||||
PathLog.error(translate("Path", "Parent job %s doesn't have a base object") % job.Label)
|
||||
PathLog.error(
|
||||
translate("Path", "Parent job %s doesn't have a base object")
|
||||
% job.Label
|
||||
)
|
||||
return False
|
||||
self.job = job
|
||||
self.model = job.Model.Group
|
||||
@@ -397,15 +579,15 @@ class ObjectOp(object):
|
||||
return True
|
||||
|
||||
def getJob(self, obj):
|
||||
'''getJob(obj) ... return the job this operation is part of.'''
|
||||
if not hasattr(self, 'job') or self.job is None:
|
||||
"""getJob(obj) ... return the job this operation is part of."""
|
||||
if not hasattr(self, "job") or self.job is None:
|
||||
if not self._setBaseAndStock(obj):
|
||||
return None
|
||||
return self.job
|
||||
|
||||
def updateDepths(self, obj, ignoreErrors=False):
|
||||
'''updateDepths(obj) ... base implementation calculating depths depending on base geometry.
|
||||
Should not be overwritten.'''
|
||||
"""updateDepths(obj) ... base implementation calculating depths depending on base geometry.
|
||||
Should not be overwritten."""
|
||||
|
||||
def faceZmin(bb, fbb):
|
||||
if fbb.ZMax == fbb.ZMin and fbb.ZMax == bb.ZMax: # top face
|
||||
@@ -428,7 +610,7 @@ class ObjectOp(object):
|
||||
obj.OpStockZMin = zmin
|
||||
obj.OpStockZMax = zmax
|
||||
|
||||
if hasattr(obj, 'Base') and obj.Base:
|
||||
if hasattr(obj, "Base") and obj.Base:
|
||||
for base, sublist in obj.Base:
|
||||
bb = base.Shape.BoundBox
|
||||
zmax = max(zmax, bb.ZMax)
|
||||
@@ -456,7 +638,9 @@ class ObjectOp(object):
|
||||
zmin = obj.OpFinalDepth.Value
|
||||
|
||||
def minZmax(z):
|
||||
if hasattr(obj, 'StepDown') and not PathGeom.isRoughly(obj.StepDown.Value, 0):
|
||||
if hasattr(obj, "StepDown") and not PathGeom.isRoughly(
|
||||
obj.StepDown.Value, 0
|
||||
):
|
||||
return z + obj.StepDown.Value
|
||||
else:
|
||||
return z + 1
|
||||
@@ -476,21 +660,23 @@ class ObjectOp(object):
|
||||
self.opUpdateDepths(obj)
|
||||
|
||||
def sanitizeBase(self, obj):
|
||||
'''sanitizeBase(obj) ... check if Base is valid and clear on errors.'''
|
||||
if hasattr(obj, 'Base'):
|
||||
"""sanitizeBase(obj) ... check if Base is valid and clear on errors."""
|
||||
if hasattr(obj, "Base"):
|
||||
try:
|
||||
for (o, sublist) in obj.Base:
|
||||
for sub in sublist:
|
||||
e = o.Shape.getElement(sub)
|
||||
except Part.OCCError as e:
|
||||
PathLog.error("{} - stale base geometry detected - clearing.".format(obj.Label))
|
||||
o.Shape.getElement(sub)
|
||||
except Part.OCCError:
|
||||
PathLog.error(
|
||||
"{} - stale base geometry detected - clearing.".format(obj.Label)
|
||||
)
|
||||
obj.Base = []
|
||||
return True
|
||||
return False
|
||||
|
||||
@waiting_effects
|
||||
def execute(self, obj):
|
||||
'''execute(obj) ... base implementation - do not overwrite!
|
||||
"""execute(obj) ... base implementation - do not overwrite!
|
||||
Verifies that the operation is assigned to a job and that the job also has a valid Base.
|
||||
It also sets the following instance variables that can and should be safely be used by
|
||||
implementation of opExecute():
|
||||
@@ -508,7 +694,7 @@ class ObjectOp(object):
|
||||
opExecute(obj) - which is expected to add the generated commands to self.commandlist
|
||||
Finally the base implementation adds a rapid move to clearance height and assigns
|
||||
the receiver's Path property from the command list.
|
||||
'''
|
||||
"""
|
||||
PathLog.track()
|
||||
|
||||
if not obj.Active:
|
||||
@@ -523,13 +709,22 @@ class ObjectOp(object):
|
||||
self.sanitizeBase(obj)
|
||||
|
||||
if FeatureCoolant & self.opFeatures(obj):
|
||||
if not hasattr(obj, 'CoolantMode'):
|
||||
PathLog.error(translate("Path", "No coolant property found. Please recreate operation."))
|
||||
if not hasattr(obj, "CoolantMode"):
|
||||
PathLog.error(
|
||||
translate(
|
||||
"Path", "No coolant property found. Please recreate operation."
|
||||
)
|
||||
)
|
||||
|
||||
if FeatureTool & self.opFeatures(obj):
|
||||
tc = obj.ToolController
|
||||
if tc is None or tc.ToolNumber == 0:
|
||||
PathLog.error(translate("Path", "No Tool Controller is selected. We need a tool to build a Path."))
|
||||
PathLog.error(
|
||||
translate(
|
||||
"Path",
|
||||
"No Tool Controller is selected. We need a tool to build a Path.",
|
||||
)
|
||||
)
|
||||
return
|
||||
else:
|
||||
self.vertFeed = tc.VertFeed.Value
|
||||
@@ -538,7 +733,12 @@ class ObjectOp(object):
|
||||
self.horizRapid = tc.HorizRapid.Value
|
||||
tool = tc.Proxy.getTool(tc)
|
||||
if not tool or float(tool.Diameter) == 0:
|
||||
PathLog.error(translate("Path", "No Tool found or diameter is zero. We need a tool to build a Path."))
|
||||
PathLog.error(
|
||||
translate(
|
||||
"Path",
|
||||
"No Tool found or diameter is zero. We need a tool to build a Path.",
|
||||
)
|
||||
)
|
||||
return
|
||||
self.radius = float(tool.Diameter) / 2.0
|
||||
self.tool = tool
|
||||
@@ -558,7 +758,9 @@ class ObjectOp(object):
|
||||
|
||||
if self.commandlist and (FeatureHeights & self.opFeatures(obj)):
|
||||
# Let's finish by rapid to clearance...just for safety
|
||||
self.commandlist.append(Path.Command("G0", {"Z": obj.ClearanceHeight.Value}))
|
||||
self.commandlist.append(
|
||||
Path.Command("G0", {"Z": obj.ClearanceHeight.Value})
|
||||
)
|
||||
|
||||
path = Path.Path(self.commandlist)
|
||||
obj.Path = path
|
||||
@@ -572,25 +774,39 @@ class ObjectOp(object):
|
||||
|
||||
if tc is None or tc.ToolNumber == 0:
|
||||
PathLog.error(translate("Path", "No Tool Controller selected."))
|
||||
return translate('Path', 'Tool Error')
|
||||
return translate("Path", "Tool Error")
|
||||
|
||||
hFeedrate = tc.HorizFeed.Value
|
||||
vFeedrate = tc.VertFeed.Value
|
||||
hRapidrate = tc.HorizRapid.Value
|
||||
vRapidrate = tc.VertRapid.Value
|
||||
|
||||
if (hFeedrate == 0 or vFeedrate == 0) and not PathPreferences.suppressAllSpeedsWarning():
|
||||
PathLog.warning(translate("Path", "Tool Controller feedrates required to calculate the cycle time."))
|
||||
return translate('Path', 'Feedrate Error')
|
||||
if (
|
||||
hFeedrate == 0 or vFeedrate == 0
|
||||
) and not PathPreferences.suppressAllSpeedsWarning():
|
||||
PathLog.warning(
|
||||
translate(
|
||||
"Path",
|
||||
"Tool Controller feedrates required to calculate the cycle time.",
|
||||
)
|
||||
)
|
||||
return translate("Path", "Feedrate Error")
|
||||
|
||||
if (hRapidrate == 0 or vRapidrate == 0) and not PathPreferences.suppressRapidSpeedsWarning():
|
||||
PathLog.warning(translate("Path", "Add Tool Controller Rapid Speeds on the SetupSheet for more accurate cycle times."))
|
||||
if (
|
||||
hRapidrate == 0 or vRapidrate == 0
|
||||
) and not PathPreferences.suppressRapidSpeedsWarning():
|
||||
PathLog.warning(
|
||||
translate(
|
||||
"Path",
|
||||
"Add Tool Controller Rapid Speeds on the SetupSheet for more accurate cycle times.",
|
||||
)
|
||||
)
|
||||
|
||||
# Get the cycle time in seconds
|
||||
seconds = obj.Path.getCycleTime(hFeedrate, vFeedrate, hRapidrate, vRapidrate)
|
||||
|
||||
if not seconds:
|
||||
return translate('Path', 'Cycletime Error')
|
||||
return translate("Path", "Cycletime Error")
|
||||
|
||||
# Convert the cycle time to a HH:MM:SS format
|
||||
cycleTime = time.strftime("%H:%M:%S", time.gmtime(seconds))
|
||||
@@ -613,17 +829,29 @@ class ObjectOp(object):
|
||||
|
||||
for p, el in baselist:
|
||||
if p == base and sub in el:
|
||||
PathLog.notice((translate("Path", "Base object %s.%s already in the list") + "\n") % (base.Label, sub))
|
||||
PathLog.notice(
|
||||
(
|
||||
translate("Path", "Base object %s.%s already in the list")
|
||||
+ "\n"
|
||||
)
|
||||
% (base.Label, sub)
|
||||
)
|
||||
return
|
||||
|
||||
if not self.opRejectAddBase(obj, base, sub):
|
||||
baselist.append((base, sub))
|
||||
obj.Base = baselist
|
||||
else:
|
||||
PathLog.notice((translate("Path", "Base object %s.%s rejected by operation") + "\n") % (base.Label, sub))
|
||||
PathLog.notice(
|
||||
(
|
||||
translate("Path", "Base object %s.%s rejected by operation")
|
||||
+ "\n"
|
||||
)
|
||||
% (base.Label, sub)
|
||||
)
|
||||
|
||||
def isToolSupported(self, obj, tool):
|
||||
'''toolSupported(obj, tool) ... Returns true if the op supports the given tool.
|
||||
This function can safely be overwritten by subclasses.'''
|
||||
"""toolSupported(obj, tool) ... Returns true if the op supports the given tool.
|
||||
This function can safely be overwritten by subclasses."""
|
||||
|
||||
return True
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user