From c44b7d7b54668b0079328237dd6dac88c88e2977 Mon Sep 17 00:00:00 2001 From: Russell Johnson <47639332+Russ4262@users.noreply.github.com> Date: Fri, 3 Sep 2021 16:33:39 -0500 Subject: [PATCH] Path: Fixes #4645 Fixes bug #4645. This PR adds a simple `integrityCheck()` method to Job object class. The method is called on the Gui side before openning the task panel to edit the job. The same method is also called upon document restoration. --- src/Mod/Path/PathScripts/PathJob.py | 118 +++++++++++++++++++------ src/Mod/Path/PathScripts/PathJobGui.py | 8 +- 2 files changed, 100 insertions(+), 26 deletions(-) diff --git a/src/Mod/Path/PathScripts/PathJob.py b/src/Mod/Path/PathScripts/PathJob.py index 983a4a7473..63a452c0d4 100644 --- a/src/Mod/Path/PathScripts/PathJob.py +++ b/src/Mod/Path/PathScripts/PathJob.py @@ -20,8 +20,6 @@ # * * # *************************************************************************** -from PathScripts.PathPostProcessor import PostProcessor -from PySide import QtCore import FreeCAD import PathScripts.PathLog as PathLog import PathScripts.PathPreferences as PathPreferences @@ -31,6 +29,8 @@ 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 @@ -75,7 +75,6 @@ def isResourceClone(obj, propLink, resourceName): def createResourceClone(obj, orig, name, icon): - clone = Draft.clone(orig) clone.Label = "%s-%s" % (name, orig.Label) clone.addProperty("App::PropertyString", "PathResource") @@ -102,9 +101,12 @@ Notification = NotificationClass() class ObjectJob: - def __init__(self, obj, models, templateFile=None): self.obj = obj + self.tooltip = None + self.tooltipArgs = None + obj.Proxy = self + obj.addProperty( "App::PropertyFile", "PostProcessorOutputFile", @@ -217,6 +219,15 @@ class ObjectJob: obj.PostProcessorArgs = PathPreferences.defaultPostProcessorArgs() obj.GeometryTolerance = PathPreferences.defaultGeometryTolerance() + self.setupOperations(obj) + self.setupSetupSheet(obj) + self.setupBaseModel(obj, models) + self.setupToolTable(obj) + self.setFromTemplateFile(obj, templateFile) + self.setupStock(obj) + + def setupOperations(self, obj): + """setupOperations(obj)... setup the Operations group for the Job object.""" ops = FreeCAD.ActiveDocument.addObject( "Path::FeatureCompoundPython", "Operations" ) @@ -228,25 +239,6 @@ class ObjectJob: obj.setEditorMode("Operations", 2) # hide obj.setEditorMode("Placement", 2) - self.setupSetupSheet(obj) - self.setupBaseModel(obj, models) - self.setupToolTable(obj) - - self.tooltip = None - self.tooltipArgs = None - - obj.Proxy = self - - self.setFromTemplateFile(obj, templateFile) - if not obj.Stock: - stockTemplate = PathPreferences.defaultStockTemplate() - if stockTemplate: - obj.Stock = PathStock.CreateFromTemplate(obj, json.loads(stockTemplate)) - if not obj.Stock: - obj.Stock = PathStock.CreateFromBase(obj) - if obj.Stock.ViewObject: - obj.Stock.ViewObject.Visibility = False - def setupSetupSheet(self, obj): if not getattr(obj, "SetupSheet", None): obj.addProperty( @@ -264,10 +256,13 @@ class ObjectJob: PathScripts.PathIconViewProvider.Attach( obj.SetupSheet.ViewObject, "SetupSheet" ) + obj.SetupSheet.Label = "SetupSheet" self.setupSheet = obj.SetupSheet.Proxy def setupBaseModel(self, obj, models=None): PathLog.track(obj.Label, models) + addModels = False + if not hasattr(obj, "Model"): obj.addProperty( "App::PropertyLink", @@ -277,6 +272,11 @@ class ObjectJob: "PathJob", "The base objects for all operations" ), ) + addModels = True + elif obj.Model is None: + addModels = True + + if addModels: model = FreeCAD.ActiveDocument.addObject( "App::DocumentObjectGroup", "Model" ) @@ -287,6 +287,7 @@ class ObjectJob: [createModelResourceClone(obj, base) for base in models] ) obj.Model = model + obj.Model.Label = "Model" if hasattr(obj, "Base"): PathLog.info( @@ -297,6 +298,7 @@ class ObjectJob: obj.removeProperty("Base") def setupToolTable(self, obj): + addTable = False if not hasattr(obj, "Tools"): obj.addProperty( "App::PropertyLink", @@ -306,6 +308,11 @@ class ObjectJob: "PathJob", "Collection of all tool controllers for the job" ), ) + addTable = True + elif obj.Tools is None: + addTable = True + + if addTable: toolTable = FreeCAD.ActiveDocument.addObject( "App::DocumentObjectGroup", "Tools" ) @@ -317,6 +324,17 @@ class ObjectJob: obj.removeProperty("ToolController") obj.Tools = toolTable + def setupStock(self, obj): + """setupStock(obj)... setup the Stock for the Job object.""" + if not obj.Stock: + stockTemplate = PathPreferences.defaultStockTemplate() + if stockTemplate: + obj.Stock = PathStock.CreateFromTemplate(obj, json.loads(stockTemplate)) + if not obj.Stock: + obj.Stock = PathStock.CreateFromBase(obj) + if obj.Stock.ViewObject: + obj.Stock.ViewObject.Visibility = False + def removeBase(self, obj, base, removeFromModel): if isResourceClone(obj, base, None): PathUtil.clearExpressionEngine(base) @@ -403,13 +421,17 @@ class ObjectJob: obj.Operations.Group = [] obj.Operations = ops FreeCAD.ActiveDocument.removeObject(name) - ops.Label = label + if label == "Unnamed": + ops.Label = "Operations" + else: + ops.Label = label def onDocumentRestored(self, obj): self.setupBaseModel(obj) self.fixupOperations(obj) self.setupSetupSheet(obj) self.setupToolTable(obj) + self.integrityCheck(obj) obj.setEditorMode("Operations", 2) # hide obj.setEditorMode("Placement", 2) @@ -624,7 +646,10 @@ class ObjectJob: def nextToolNumber(self): # returns the next available toolnumber in the job group = self.obj.Tools.Group - return sorted([t.ToolNumber for t in group])[-1] + 1 + if len(group) > 0: + return sorted([t.ToolNumber for t in group])[-1] + 1 + else: + return 1 def addToolController(self, tc): group = self.obj.Tools.Group @@ -680,6 +705,49 @@ class ObjectJob: for op in self.allOperations(): op.Path.Center = center + def integrityCheck(self, job): + """integrityCheck(job)... Return True if job has all expected children objects. Attempts to restore any missing children.""" + suffix = "" + if len(job.Name) > 3: + suffix = job.Name[3:] + + def errorMessage(grp, job): + PathLog.error( + translate("PathJobGui", "{} corrupt in {} job.".format(grp, job.Name)) + ) + + if not job.Operations: + self.setupOperations(job) + job.Operations.Label = "Operations" + suffix + if not job.Operations: + errorMessage("Operations", job) + return False + if not job.SetupSheet: + self.setupSetupSheet(job) + job.SetupSheet.Label = "SetupSheet" + suffix + if not job.SetupSheet: + errorMessage("SetupSheet", job) + return False + if not job.Model: + self.setupBaseModel(job) + job.Model.Label = "Model" + suffix + if not job.Model: + errorMessage("Model", job) + return False + if not job.Stock: + self.setupStock(job) + job.Stock.Label = "Stock" + suffix + if not job.Stock: + errorMessage("Stock", job) + return False + if not job.Tools: + self.setupToolTable(job) + job.Tools.Label = "Tools" + suffix + if not job.Tools: + errorMessage("Tools", job) + return False + return True + @classmethod def baseCandidates(cls): """Answer all objects in the current document which could serve as a Base for a job.""" diff --git a/src/Mod/Path/PathScripts/PathJobGui.py b/src/Mod/Path/PathScripts/PathJobGui.py index cc322b5069..ff040ce98a 100644 --- a/src/Mod/Path/PathScripts/PathJobGui.py +++ b/src/Mod/Path/PathScripts/PathJobGui.py @@ -167,6 +167,9 @@ class ViewProvider: def setEdit(self, vobj=None, mode=0): PathLog.track(mode) if 0 == mode: + job = self.vobj.Object + if not job.Proxy.integrityCheck(job): + return False self.openTaskPanel() return True @@ -1413,7 +1416,10 @@ class TaskPanel: def setupUi(self, activate): self.setupGlobal.setupUi() - self.setupOps.setupUi() + try: + self.setupOps.setupUi() + except Exception as ee: + PathLog.error(str(ee)) self.updateStockEditor(-1, False) self.setFields()