def unlinkObject(self, featureToUnlink): '''Redirects all links to this object, to make it possible to recompute it individually. TODO: expressions!!''' doc = featureToUnlink.Document ListOfDependentObjects = featureToUnlink.InList #<rant> naming list of objects that are dependent on the feature as InList is bullshit. InList should have been list of inputs - that is, list of objects this feature depends on. But it's back-to-front. =< if len(self.actionList) > 0: raise ValueError("unlinker hasn't restored the changes it did previously. Can't unlink another object.") for obj in ListOfDependentObjects: for propname in obj.PropertiesList: try: typ = obj.getTypeIdOfProperty(propname) val = getattr(obj,propname) bool_changed = True if typ == 'App::PropertyLink': setattr(obj,propname,None) elif typ == 'App::PropertyLinkSub': #val is (feature,["Edge1","Face2"]) setattr(obj,propname,None) elif typ == 'App::PropertyLinkList': setattr(obj,propname,[]) elif typ == 'App::PropertyLinkSubList': setattr(obj,propname,[]) else: bool_changed = False if bool_changed: self.actionList.append((obj,propname,val)) except Exception as err: LE.error(None, "While temporarily removing all links to an object, an error occured.\n" + "Error:"+err.message+"\n" + "object = "+obj.Name+"\n" + "property = "+propname+"\n" + "value to be restored = "+repr(value))
def restoreLinks(self): for (obj, propname, value) in self.actionList: try: setattr(obj,propname,value) except Exception as err: LE.error(None, "An error occured while restoring links.\n" + "Error:"+err.message+"\n" + "object = "+obj.Name+"\n" + "property = "+propname+"\n" + "value to be restored = "+repr(value)) self.actionList = []
def derivedExecute(self, selfobj): if selfobj.Recomputing == "Disabled": raise ValueError( selfobj.Name + ": recomputing of this object is currently disabled. Modify 'Recomputing' property to enable it." ) try: # do the subsequencing in this document first, to verify stuff is set up correctly, and to obtain sequence length if self.isVerbose(): print("In-place pre-subsequencing, for early check") n_seq, subs_linkdict = self.makeSubsequence( selfobj, screen(selfobj.ObjectToLoopOver)) bGui = bool( App.GuiUp ) and Executer.globalIsCreatingLatticeFeature #disabled for most recomputes, because it causes a crash if property edits are approved by hitting Enter if bGui: import PySide progress = PySide.QtGui.QProgressDialog( u"Recomputing " + selfobj.Label, u"Abort", 0, n_seq + 1) progress.setModal(True) progress.show() doc1 = selfobj.Document doc2 = App.newDocument() object_to_take_in_doc2 = None # define the variable, to prevent del() in finally block from raising another error object_to_loop_in_doc2 = None try: if self.isVerbose(): print( "Copying object with dependencies to a temporary document..." ) doc2.copyObject(screen(selfobj.ObjectToTake), True) if self.isVerbose(): print("Enabling nested para/toposeries, if any...") #if there are nested para/toposeries in the dependencies, make sure to enable them for objd2 in doc2.Objects: if hasattr(objd2, "Recomputing"): try: objd2.Recomputing = "Enabled" objd2.purgeTouched() except exception: Executer.warning( selfobj, "Failed to enable recomputing of " + objd2.Name) object_to_take_in_doc2 = doc2.getObject( screen(selfobj.ObjectToTake).Name) object_to_loop_in_doc2 = doc2.getObject( screen(selfobj.ObjectToLoopOver).Name) if bGui: progress.setValue(1) if self.isVerbose(): print("Repeating subsequencing in temporary document...") n_seq, subs_linkdict = self.makeSubsequence( selfobj, object_to_loop_in_doc2) output_shapes = [] for i in range(n_seq): if self.isVerbose(): print("Computing {x}/{y}".format(x=i + 1, y=n_seq)) for key in subs_linkdict: writeProperty(doc2, key[0], key[1], subs_linkdict[key][i]) #recompute doc2.recompute() #get shape shape = None for obj in doc2.Objects: if 'Invalid' in obj.State: Executer.error( obj, "Recomputing shape for subsequence index " + repr(i) + " failed.") scale = 1.0 try: if not screen( selfobj.ObjectToTake).Shape.isNull(): scale = screen( selfobj.ObjectToTake ).Shape.BoundBox.DiagonalLength / math.sqrt( 3) except Exception: pass if scale < DistConfusion * 100: scale = 1.0 shape = markers.getNullShapeShape(scale) if shape is None: shape = object_to_take_in_doc2.Shape.copy() output_shapes.append(shape) #update progress if bGui: progress.setValue(progress.value() + 1) if progress.wasCanceled(): raise Executer.CancelError() finally: #delete all references, before destroying the document. Probably not required, but to be sure... if self.isVerbose(): print("Cleanup...") del (object_to_take_in_doc2) del (object_to_loop_in_doc2) doc2_name = doc2.Name del (doc2) App.closeDocument(doc2_name) if bGui: progress.setValue(n_seq + 1) selfobj.Shape = Part.makeCompound(output_shapes) output_is_lattice = lattice2BaseFeature.isObjectLattice( screen(selfobj.ObjectToTake)) if 'Auto' in selfobj.isLattice: new_isLattice = 'Auto-On' if output_is_lattice else 'Auto-Off' if selfobj.isLattice != new_isLattice: #check, to not cause onChanged without necessity (onChange messes with colors, it's better to keep user color) selfobj.isLattice = new_isLattice finally: if selfobj.Recomputing == "Recompute Once": selfobj.Recomputing = "Disabled" return "suppress" # "suppress" disables most convenience code of lattice2BaseFeature. We do it because we build a nested array, which are not yet supported by lattice WB.
def derivedExecute(self,selfobj): # values generator should be functional even if recomputing is disabled, so do it first self.assureGenerator(selfobj) self.generator.updateReadonlyness() self.generator.execute() if selfobj.Recomputing == "Disabled": raise ValueError(selfobj.Name+": recomputing of this object is currently disabled. Modify 'Recomputing' property to enable it.") try: #test parameter references and read out their current values refstr = selfobj.ParameterRef #dict(selfobj.ExpressionEngine)["ParameterRef"] refstrs = refstr.replace(";","\t").split("\t") defvalues = [] for refstr in refstrs: refstr = refstr.strip(); val = None; try: val = getParameter(selfobj.Document,refstr) except Exception as err: App.Console.PrintError("{obj}: failed to read out parameter '{param}': {err}\n" .format(obj= selfobj.Name, param= refstr, err= err.message)) defvalues.append(val) N_params = len(defvalues) if N_params == 0: raise ValueError(selfobj.Name+": ParameterRef is not set. It is required.") #parse values values = [] for strrow in selfobj.Values: if len(strrow) == 0: break; row = strrow.split(";") row = [(strv.strip() if len(strv.strip())>0 else None) for strv in row] # clean out spaces and replace empty strings with None if len(row) < N_params: row += [None]*(N_params - len(row)) values.append(row) # convert values to type, filling in defaults where values are missing for row in values: for icol in range(N_params): strv = row[icol] val = None if strv is None: val = defvalues[icol] elif selfobj.ParameterType == 'float' or selfobj.ParameterType == 'int': val = float(strv.replace(",",".")) if selfobj.ParameterType == 'int': val = int(round(val)) elif selfobj.ParameterType == 'string': val = strv.strip() else: raise ValueError(selfobj.Name + ": ParameterType option not implemented: "+selfobj.ParameterType) row[icol] = val if len(values) == 0: scale = 1.0 try: if not selfobj.Object.Shape.isNull(): scale = selfobj.Object.Shape.BoundBox.DiagonalLength/math.sqrt(3) except Exception: pass if scale < DistConfusion * 100: scale = 1.0 selfobj.Shape = markers.getNullShapeShape(scale) raise ValueError(selfobj.Name + ": list of values is empty.") bGui = False #bool(App.GuiUp) #disabled temporarily, because it causes a crash if property edits are approved by hitting Enter if bGui: import PySide progress = PySide.QtGui.QProgressDialog(u"Recomputing "+selfobj.Label, u"Abort", 0, len(values)+1) progress.setModal(True) progress.show() doc1 = selfobj.Document doc2 = App.newDocument() object_in_doc2 = None # define the variable, to prevent del() in finally block from raising another error try: doc2.copyObject(selfobj.Object, True) #if there are nested paraseries in the dependencies, make sure to enable them for objd2 in doc2.Objects: if hasattr(objd2,"Recomputing"): try: objd2.Recomputing = "Enabled" objd2.purgeTouched() except exception: lattice2Executer.warning(selfobj,"Failed to enable recomputing of "+objd2.Name) object_in_doc2 = doc2.getObject(selfobj.Object.Name) if bGui: progress.setValue(1) output_shapes = [] for row in values: for icol in range(len(row)): setParameter(doc2, refstrs[icol].strip(), row[icol]) #recompute doc2.recompute() #get shape shape = None for obj in doc2.Objects: if 'Invalid' in obj.State: lattice2Executer.error(obj,"Recomputing shape for parameter value of "+repr(row)+" failed.") scale = 1.0 try: if not selfobj.Object.Shape.isNull(): scale = selfobj.Object.Shape.BoundBox.DiagonalLength/math.sqrt(3) except Exception: pass if scale < DistConfusion * 100: scale = 1.0 shape = markers.getNullShapeShape(scale) if shape is None: shape = object_in_doc2.Shape.copy() output_shapes.append(shape) #update progress if bGui: progress.setValue(progress.value()+1) if progress.wasCanceled(): raise lattice2Executer.CancelError() finally: #delete all references, before destroying the document. Probably not required, but to be sure... del(object_in_doc2) doc2_name = doc2.Name del(doc2) App.closeDocument(doc2_name) if bGui: progress.setValue(len(values)+1) selfobj.Shape = Part.makeCompound(output_shapes) output_is_lattice = lattice2BaseFeature.isObjectLattice(selfobj.Object) if 'Auto' in selfobj.isLattice: new_isLattice = 'Auto-On' if output_is_lattice else 'Auto-Off' if selfobj.isLattice != new_isLattice:#check, to not cause onChanged without necessity (onChange messes with colors, it's better to keep user color) selfobj.isLattice = new_isLattice finally: if selfobj.Recomputing == "Recompute Once": selfobj.Recomputing = "Disabled" return "suppress" # "suppress" disables most convenience code of lattice2BaseFeature. We do it because we build a nested array, which are not yet supported by lattice WB.
def derivedExecute(self, selfobj): # values generator should be functional even if recomputing is disabled, so do it first self.assureGenerator(selfobj) self.generator.updateReadonlyness() self.generator.execute() if selfobj.Recomputing == "Disabled": raise ValueError( selfobj.Name + ": recomputing of this object is currently disabled. Modify 'Recomputing' property to enable it." ) try: #test parameter references and read out their current values refstr = selfobj.ParameterRef #dict(selfobj.ExpressionEngine)["ParameterRef"] refstrs = refstr.replace(";", "\t").split("\t") defvalues = [] for refstr in refstrs: refstr = refstr.strip() val = None try: val = getParameter(selfobj.Document, refstr) except Exception as err: App.Console.PrintError( "{obj}: failed to read out parameter '{param}': {err}\n" .format(obj=selfobj.Name, param=refstr, err=str(err))) defvalues.append(val) N_params = len(defvalues) if N_params == 0: raise ValueError(selfobj.Name + ": ParameterRef is not set. It is required.") #parse values values = [] for strrow in selfobj.Values: if len(strrow) == 0: break row = strrow.split(";") row = [ (strv.strip() if len(strv.strip()) > 0 else None) for strv in row ] # clean out spaces and replace empty strings with None if len(row) < N_params: row += [None] * (N_params - len(row)) values.append(row) # convert values to type, filling in defaults where values are missing for row in values: for icol in range(N_params): strv = row[icol] val = None if strv is None: val = defvalues[icol] elif selfobj.ParameterType == 'float' or selfobj.ParameterType == 'int': val = float(strv.replace(",", ".")) if selfobj.ParameterType == 'int': val = int(round(val)) elif selfobj.ParameterType == 'string': val = strv.strip() else: raise ValueError( selfobj.Name + ": ParameterType option not implemented: " + selfobj.ParameterType) row[icol] = val if len(values) == 0: scale = 1.0 try: if not screen(selfobj.Object).Shape.isNull(): scale = screen( selfobj.Object ).Shape.BoundBox.DiagonalLength / math.sqrt(3) except Exception: pass if scale < DistConfusion * 100: scale = 1.0 selfobj.Shape = markers.getNullShapeShape(scale) raise ValueError(selfobj.Name + ": list of values is empty.") bGui = False #bool(App.GuiUp) #disabled temporarily, because it causes a crash if property edits are approved by hitting Enter if bGui: import PySide progress = PySide.QtGui.QProgressDialog( u"Recomputing " + selfobj.Label, u"Abort", 0, len(values) + 1) progress.setModal(True) progress.show() doc1 = selfobj.Document doc2 = App.newDocument( ) #create temporary doc to do the computations # assign doc's filename before copying objects, otherwise we get errors with xlinks try: doc2.FileName = doc1.FileName except Exception as err: pass #in old FreeCADs, FileName property is read-only, we can safely ignore that object_in_doc2 = None # define the variable, to prevent del() in finally block from raising another error try: doc2.copyObject(screen(selfobj.Object), True) #if there are nested paraseries in the dependencies, make sure to enable them for objd2 in doc2.Objects: if hasattr(objd2, "Recomputing"): try: objd2.Recomputing = "Enabled" objd2.purgeTouched() except exception: lattice2Executer.warning( selfobj, "Failed to enable recomputing of " + objd2.Name) object_in_doc2 = doc2.getObject(screen(selfobj.Object).Name) if bGui: progress.setValue(1) output_shapes = [] for row in values: for icol in range(len(row)): setParameter(doc2, refstrs[icol].strip(), row[icol]) #recompute doc2.recompute() #get shape shape = None for obj in doc2.Objects: if 'Invalid' in obj.State: lattice2Executer.error( obj, "Recomputing shape for parameter value of " + repr(row) + " failed.") scale = 1.0 try: if not screen(selfobj.Object).Shape.isNull(): scale = screen( selfobj.Object ).Shape.BoundBox.DiagonalLength / math.sqrt( 3) except Exception: pass if scale < DistConfusion * 100: scale = 1.0 shape = markers.getNullShapeShape(scale) if shape is None: shape = object_in_doc2.Shape.copy() output_shapes.append(shape) #update progress if bGui: progress.setValue(progress.value() + 1) if progress.wasCanceled(): raise lattice2Executer.CancelError() finally: #delete all references, before destroying the document. Probably not required, but to be sure... del (object_in_doc2) doc2_name = doc2.Name del (doc2) App.closeDocument(doc2_name) if bGui: progress.setValue(len(values) + 1) selfobj.Shape = Part.makeCompound(output_shapes) output_is_lattice = lattice2BaseFeature.isObjectLattice( screen(selfobj.Object)) if 'Auto' in selfobj.isLattice: new_isLattice = 'Auto-On' if output_is_lattice else 'Auto-Off' if selfobj.isLattice != new_isLattice: #check, to not cause onChanged without necessity (onChange messes with colors, it's better to keep user color) selfobj.isLattice = new_isLattice finally: if selfobj.Recomputing == "Recompute Once": selfobj.Recomputing = "Disabled" return "suppress" # "suppress" disables most convenience code of lattice2BaseFeature. We do it because we build a nested array, which are not yet supported by lattice WB.