diff --git a/src/Mod/Path/PathScripts/PathJob.py b/src/Mod/Path/PathScripts/PathJob.py index f8855d2344..4b493caca8 100644 --- a/src/Mod/Path/PathScripts/PathJob.py +++ b/src/Mod/Path/PathScripts/PathJob.py @@ -30,7 +30,8 @@ import PathScripts.PathSetupSheet as PathSetupSheet import PathScripts.PathStock as PathStock import PathScripts.PathToolController as PathToolController import PathScripts.PathUtil as PathUtil -import json, time +import json +import time # lazily loaded modules from lazy_loader.lazy_loader import LazyLoader @@ -41,13 +42,13 @@ from PathScripts.PathPostProcessor import PostProcessor from PySide import QtCore PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule(PathLog.thisModule()) # Qt translation handling def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) + class JobTemplate: # pylint: disable=no-init '''Attribute and sub element strings for template export/import.''' @@ -62,15 +63,18 @@ class JobTemplate: ToolController = 'ToolController' Version = 'Version' + def isArchPanelSheet(obj): return hasattr(obj, 'Proxy') and isinstance(obj.Proxy, ArchPanel.PanelSheet) + def isResourceClone(obj, propLink, resourceName): # pylint: disable=unused-argument 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 @@ -84,22 +88,24 @@ def createResourceClone(obj, orig, name, icon): PathIconViewProvider.Attach(clone.ViewObject, icon) clone.ViewObject.Visibility = False clone.ViewObject.Transparency = 80 - obj.Document.recompute() # necessary to create the clone shape + obj.Document.recompute() # necessary to create the clone shape return clone + def createModelResourceClone(obj, orig): return createResourceClone(obj, orig, 'Model', 'BaseGeometry') + class ObjectJob: - def __init__(self, obj, models, templateFile = None): + 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::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", "Description", "Path", QtCore.QT_TRANSLATE_NOOP("PathJob","An optional description for this job")) - obj.addProperty("App::PropertyString", "CycleTime", "Path", QtCore.QT_TRANSLATE_NOOP("PathOp", "Operations Cycle Time Estimation")) + 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")) @@ -107,14 +113,13 @@ class ObjectJob: 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::PropertyLinkList", "ToolController", "Base", QtCore.QT_TRANSLATE_NOOP("PathJob", "Collection of tool controllers available for this job.")) - 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.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.setEditorMode("PostProcessorOutputFile", 0) # set to default mode obj.PostProcessor = postProcessors = PathPreferences.allEnabledPostProcessors() defaultPostProcessor = PathPreferences.defaultPostProcessor() # Check to see if default post processor hasn't been 'lost' (This can happen when Macro dir has changed) @@ -131,7 +136,7 @@ class ObjectJob: ops.ViewObject.Visibility = False obj.Operations = ops - obj.setEditorMode('Operations', 2) # hide + obj.setEditorMode('Operations', 2) # hide obj.setEditorMode('Placement', 2) self.setupSetupSheet(obj) @@ -235,7 +240,7 @@ class ObjectJob: if obj.Operations.ViewObject: try: obj.Operations.ViewObject.DisplayMode - except Exception: # pylint: disable=broad-except + except Exception: # pylint: disable=broad-except name = obj.Operations.Name label = obj.Operations.Label ops = FreeCAD.ActiveDocument.addObject("Path::FeatureCompoundPython", "Operations") @@ -246,12 +251,11 @@ class ObjectJob: FreeCAD.ActiveDocument.removeObject(name) ops.Label = label - def onDocumentRestored(self, obj): self.setupBaseModel(obj) self.fixupOperations(obj) self.setupSetupSheet(obj) - obj.setEditorMode('Operations', 2) # hide + obj.setEditorMode('Operations', 2) # hide obj.setEditorMode('Placement', 2) if not hasattr(obj, 'CycleTime'): @@ -327,13 +331,13 @@ class ObjectJob: attrs = {} attrs[JobTemplate.Version] = 1 if obj.PostProcessor: - attrs[JobTemplate.PostProcessor] = obj.PostProcessor - attrs[JobTemplate.PostProcessorArgs] = obj.PostProcessorArgs + attrs[JobTemplate.PostProcessor] = obj.PostProcessor + attrs[JobTemplate.PostProcessorArgs] = obj.PostProcessorArgs if obj.PostProcessorOutputFile: attrs[JobTemplate.PostProcessorOutputFile] = obj.PostProcessorOutputFile - attrs[JobTemplate.GeometryTolerance] = str(obj.GeometryTolerance.Value) + attrs[JobTemplate.GeometryTolerance] = str(obj.GeometryTolerance.Value) if obj.Description: - attrs[JobTemplate.Description] = obj.Description + attrs[JobTemplate.Description] = obj.Description return attrs def __getstate__(self): @@ -367,19 +371,18 @@ class ObjectJob: formattedCycleTime = PathUtil.opProperty(op, 'CycleTime') opCycleTime = 0 try: - ## convert the formatted time from HH:MM:SS to just seconds + # 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(":")))) except: - FreeCAD.Console.PrintWarning("Error converting the operations cycle time. Job Cycle time may be innacturate\n") continue if opCycleTime > 0: seconds = seconds + opCycleTime - cycleTimeString = time.strftime("%H:%M:%S", time.gmtime(seconds)) - self.obj.CycleTime = cycleTimeString + cycleTimeString = time.strftime("%H:%M:%S", time.gmtime(seconds)) + self.obj.CycleTime = cycleTimeString - def addOperation(self, op, before = None, removeBefore = False): + def addOperation(self, op, before=None, removeBefore=False): group = self.obj.Operations.Group if op not in group: if before: @@ -387,7 +390,7 @@ class ObjectJob: group.insert(group.index(before), op) if removeBefore: group.remove(before) - except Exception as e: # pylint: disable=broad-except + except Exception as e: # pylint: disable=broad-except PathLog.error(e) group.append(op) else: @@ -399,13 +402,14 @@ 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.Template.VertRapid)) + 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 def allOperations(self): ops = [] + def collectBaseOps(op): if hasattr(op, 'TypeId'): if op.TypeId == 'Path::FeaturePython': @@ -437,13 +441,15 @@ class ObjectJob: '''Answer true if the given object can be used as a Base for a job.''' return PathUtil.isValidBaseObject(obj) or isArchPanelSheet(obj) + def Instances(): '''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 [] -def Create(name, base, templateFile = None): + +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.''' if str == type(base[0]): @@ -455,4 +461,3 @@ def Create(name, base, templateFile = None): obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) obj.Proxy = ObjectJob(obj, models, templateFile) return obj - diff --git a/src/Mod/Path/PathScripts/PathOp.py b/src/Mod/Path/PathScripts/PathOp.py index 97a8f0fa19..a4d3a7807f 100644 --- a/src/Mod/Path/PathScripts/PathOp.py +++ b/src/Mod/Path/PathScripts/PathOp.py @@ -43,13 +43,13 @@ __url__ = "http://www.freecadweb.org" __doc__ = "Base class and properties implementation for all Path operations." PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) -#PathLog.trackModule() # Qt translation handling def translate(context, text, disambig=None): return QtCore.QCoreApplication.translate(context, text, disambig) + FeatureTool = 0x0001 # ToolController FeatureDepths = 0x0002 # FinalDepth, StartDepth FeatureHeights = 0x0004 # ClearanceHeight, SafeHeight @@ -89,7 +89,7 @@ class ObjectOp(object): FeatureBaseFaces ... Base geometry support for faces FeatureBasePanels ... Base geometry support for Arch.Panels FeatureLocations ... Base location support - FeatureCoolant ... Support for operation coolant + FeatureCoolant ... Support for operation coolant The base class handles all base API and forwards calls to subclasses with an op prefix. For instance, an op is not expected to overwrite onChanged(), @@ -140,7 +140,7 @@ class ObjectOp(object): if FeatureCoolant & features: 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")) @@ -194,7 +194,7 @@ class ObjectOp(object): for op in ['OpStartDepth', 'OpFinalDepth', 'OpToolDiameter', 'CycleTime']: if hasattr(obj, op): - obj.setEditorMode(op, 1) # read-only + obj.setEditorMode(op, 1) # read-only if FeatureDepths & features: if FeatureNoFinalDepth & features: @@ -255,12 +255,12 @@ class ObjectOp(object): def initOperation(self, obj): '''initOperation(obj) ... implement to create additional properties. Should be overwritten by subclasses.''' - pass # pylint: disable=unnecessary-pass + 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.''' - pass # pylint: disable=unnecessary-pass + pass # pylint: disable=unnecessary-pass def opOnChanged(self, obj, prop): '''opOnChanged(obj, prop) ... overwrite to process property changes. @@ -269,24 +269,24 @@ class ObjectOp(object): distinguish between assigning a different value and assigning the same value again. Can safely be overwritten by subclasses.''' - pass # pylint: disable=unnecessary-pass + pass # pylint: disable=unnecessary-pass def opSetDefaultValues(self, obj, job): '''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.''' - pass # pylint: disable=unnecessary-pass + pass # pylint: disable=unnecessary-pass def opUpdateDepths(self, obj): '''opUpdateDepths(obj) ... overwrite to implement special depths calculation. Can safely be overwritten by subclass.''' - pass # pylint: disable=unnecessary-pass + pass # pylint: disable=unnecessary-pass def opExecute(self, obj): '''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.''' - pass # pylint: disable=unnecessary-pass + 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. @@ -297,12 +297,11 @@ class ObjectOp(object): def onChanged(self, obj, prop): '''onChanged(obj, prop) ... base implementation of the FC notification framework. Do not overwrite, overwrite opOnChanged() instead.''' - if not 'Restore' 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''' if expr: @@ -483,18 +482,17 @@ class ObjectOp(object): obj.Path = path return - if not self._setBaseAndStock(obj): return if FeatureCoolant & self.opFeatures(obj): if not hasattr(obj, 'CoolantMode'): - FreeCAD.Console.PrintError("No coolant property found. Please recreate operation.") + 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: - FreeCAD.Console.PrintError("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 @@ -503,9 +501,9 @@ class ObjectOp(object): self.horizRapid = tc.HorizRapid.Value tool = tc.Proxy.getTool(tc) if not tool or float(tool.Diameter) == 0: - FreeCAD.Console.PrintError("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 + self.radius = float(tool.Diameter) / 2 self.tool = tool obj.OpToolDiameter = tool.Diameter @@ -519,7 +517,7 @@ class ObjectOp(object): if obj.Comment: self.commandlist.append(Path.Command("(%s)" % obj.Comment)) - result = self.opExecute(obj) # pylint: disable=assignment-from-no-return + result = self.opExecute(obj) # pylint: disable=assignment-from-no-return if FeatureHeights & self.opFeatures(obj): # Let's finish by rapid to clearance...just for safety @@ -536,8 +534,8 @@ class ObjectOp(object): tc = obj.ToolController if tc is None or tc.ToolNumber == 0: - FreeCAD.Console.PrintError("No Tool Controller is selected. Tool feed rates required to calculate the cycle time.\n") - return translate('PathGui', 'Tool Error') + PathLog.error(translate("Path", "No Tool Controller selected.")) + return translate('Path', 'Tool Error') hFeedrate = tc.HorizFeed.Value vFeedrate = tc.VertFeed.Value @@ -545,19 +543,19 @@ class ObjectOp(object): vRapidrate = tc.VertRapid.Value if hFeedrate == 0 or vFeedrate == 0: - FreeCAD.Console.PrintError("Tool Controller requires feed rates. Tool feed rates required to calculate the cycle time.\n") - return translate('PathGui', 'Feedrate Error') + PathLog.warning(translate("Path", "Tool Controller feedrates required to calculate the cycle time.")) + return translate('Path', 'Feedrate Error') if hRapidrate == 0 or vRapidrate == 0: - FreeCAD.Console.PrintWarning("Add Tool Controller Rapid Speeds on the SetupSheet for more accurate cycle times.\n") + PathLog.warning(translate("Path", "Add Tool Controller Rapid Speeds on the SetupSheet for more accurate cycle times.")) - ## get the cycle time in seconds + # Get the cycle time in seconds seconds = obj.Path.getCycleTime(hFeedrate, vFeedrate, hRapidrate, vRapidrate) - + if not seconds: - return translate('PathGui', 'Cycletime Error') - - ## convert the cycle time to a HH:MM:SS format + return translate('Path', 'Cycletime Error') + + # Convert the cycle time to a HH:MM:SS format cycleTime = time.strftime("%H:%M:%S", time.gmtime(seconds)) return cycleTime @@ -578,11 +576,11 @@ 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))