def createUpdateFileList( importPath, parentAssemblyDir, filesToUpdate, recursive=False, selectedFiles=[] #only update parts with these sourceFiles ): # do not update converted parts print("createUpdateFileList importPath = {}".format(importPath)) if a2plib.to_bytes(importPath) == b'converted': return False, filesToUpdate fileNameInProject = a2plib.findSourceFileInProject(importPath, parentAssemblyDir) workingDir, basicFileName = os.path.split(fileNameInProject) docReader1 = FCdocumentReader() docReader1.openDocument(fileNameInProject) needToUpdate = False subAsmNeedsUpdate = False for ob in docReader1.getA2pObjects(): if a2plib.to_bytes(ob.getA2pSource()) == b'converted': print("Did not update converted part '{}'".format(ob.name)) continue #Only update parts which are selected by the user... fDir, fName = os.path.split(ob.getA2pSource()) if len(selectedFiles) > 0 and fName not in selectedFiles: continue if ob.isSubassembly() and recursive: subAsmNeedsUpdate, filesToUpdate = createUpdateFileList( ob.getA2pSource(), workingDir, filesToUpdate, recursive) if subAsmNeedsUpdate: needToUpdate = True objFileNameInProject = a2plib.findSourceFileInProject( ob.getA2pSource(), workingDir) mtime = os.path.getmtime(objFileNameInProject) if ob.getTimeLastImport() < mtime: needToUpdate = True if needToUpdate: if fileNameInProject not in filesToUpdate: filesToUpdate.append(fileNameInProject) return needToUpdate, filesToUpdate
def Activated(self): selection = [s for s in FreeCADGui.Selection.getSelection() if s.Document == FreeCAD.ActiveDocument ] obj = selection[0] FreeCADGui.Selection.clearSelection() # very imporant! Avoid Editing the assembly the part was called from! fileNameWithinProjectFile = a2plib.findSourceFileInProject(obj.sourceFile) if fileNameWithinProjectFile == None: msg = \ ''' You want to edit a file which is not found below your project-folder. This is not allowed when using preference "Use project Folder" ''' QtGui.QMessageBox.critical( QtGui.QApplication.activeWindow(), "File error ! ", msg ) return docs = FreeCAD.listDocuments().values() docFilenames = [ d.FileName for d in docs ] if not fileNameWithinProjectFile in docFilenames : FreeCAD.open(fileNameWithinProjectFile) else: name = docs[docFilenames.index(fileNameWithinProjectFile)].Name FreeCAD.setActiveDocument( name ) FreeCAD.ActiveDocument=FreeCAD.getDocument( name ) FreeCADGui.ActiveDocument=FreeCADGui.getDocument( name )
def updateImportedParts(doc): objectCache.cleanUp(doc) for obj in doc.Objects: if hasattr(obj, 'sourceFile'): if not hasattr( obj, 'timeLastImport'): obj.addProperty("App::PropertyFloat", "timeLastImport","importPart") #should default to zero which will force update. obj.setEditorMode("timeLastImport",1) if not hasattr( obj, 'a2p_Version'): obj.addProperty("App::PropertyString", "a2p_Version","importPart").a2p_Version = 'V0.0' obj.setEditorMode("a2p_Version",1) if not hasattr( obj, 'muxInfo'): obj.addProperty("App::PropertyStringList","muxInfo","importPart").muxInfo = [] if a2plib.USE_PROJECTFILE: replacement = a2plib.findSourceFileInProject(obj.sourceFile) # work in any case with files within projectFolder! else: replacement = obj.sourceFile if replacement == None: QtGui.QMessageBox.critical( QtGui.QApplication.activeWindow(), "Source file not found", "update of %s aborted!\nUnable to find %s" % ( obj.Name, obj.sourceFile ) ) else: obj.sourceFile = replacement # update Filepath, perhaps location changed ! if os.path.exists( obj.sourceFile ): newPartCreationTime = os.path.getmtime( obj.sourceFile ) if ( newPartCreationTime > obj.timeLastImport or obj.a2p_Version != A2P_VERSION ): if not objectCache.isCached(obj.sourceFile): # Load every changed object one time to cache importPartFromFile(doc, obj.sourceFile, importToCache=True) # the version is now in the cache newObject = objectCache.get(obj.sourceFile) obj.timeLastImport = newPartCreationTime if hasattr(newObject, 'a2p_Version'): obj.a2p_Version = newObject.a2p_Version importUpdateConstraintSubobjects( doc, obj, newObject )# do this before changing shape and mux if hasattr(newObject, 'muxInfo'): obj.muxInfo = newObject.muxInfo # save Placement becaause following newObject.Shape.copy() ist resetting it to zeroes... savedPlacement = obj.Placement obj.Shape = newObject.Shape.copy() obj.ViewObject.DiffuseColor = copy.copy(newObject.ViewObject.DiffuseColor) obj.Placement = savedPlacement # restore the old placement mw = FreeCADGui.getMainWindow() mdi = mw.findChild(QtGui.QMdiArea) sub = mdi.activeSubWindow() sub.showMaximized() objectCache.cleanUp(doc) a2p_solversystem.autoSolveConstraints(doc) doc.recompute()
def Activated(self): doc = FreeCAD.activeDocument() if doc == None: QtGui.QMessageBox.information( QtGui.QApplication.activeWindow(), "No active document found!", "Before editing a part, you have to open an assembly file.") return selection = [ s for s in FreeCADGui.Selection.getSelection() if s.Document == FreeCAD.ActiveDocument ] if not selection: msg = \ ''' You must select a part to edit first. ''' QtGui.QMessageBox.information(QtGui.QApplication.activeWindow(), "Selection Error", msg) return obj = selection[0] FreeCADGui.Selection.clearSelection( ) # very important! Avoid Editing the assembly the part was called from! assemblyPath = os.path.normpath(os.path.split(doc.FileName)[0]) fileNameWithinProjectFile = a2plib.findSourceFileInProject( obj.sourceFile, assemblyPath) if fileNameWithinProjectFile == None: msg = \ ''' You want to edit a file which is not found below your project-folder. This is not allowed when using preference "Use project Folder" ''' QtGui.QMessageBox.critical(QtGui.QApplication.activeWindow(), "File error ! ", msg) return #TODO: WF fails if "use folder" = false here docs = [] for d in FreeCAD.listDocuments().values( ): #dict_values not indexable, docs now is... docs.append(d) #docs = FreeCAD.listDocuments().values() docFilenames = [d.FileName for d in docs] if not fileNameWithinProjectFile in docFilenames: FreeCAD.open(fileNameWithinProjectFile) else: idx = docFilenames.index(fileNameWithinProjectFile) name = docs[idx].Name # Search and activate the corresponding document window.. mw = FreeCADGui.getMainWindow() mdi = mw.findChild(QtGui.QMdiArea) sub = mdi.subWindowList() for s in sub: mdi.setActiveSubWindow(s) if FreeCAD.activeDocument().Name == name: break
def createPartList( importPath, parentAssemblyDir, partListEntries, recursive=False ): ''' Extract quantities and descriptions of assembled parts from document.xml Is able to analyse subassemblies by recursion It works with a dict. Structure of an entry is: filename: [Quantity,[information,information,....] ] ''' fileNameInProject = a2plib.findSourceFileInProject( importPath, parentAssemblyDir ) workingDir,basicFileName = os.path.split(fileNameInProject) docReader1 = FCdocumentReader() docReader1.openDocument(fileNameInProject) for ob in docReader1.getA2pObjects(): # skip converted parts... if a2plib.to_str(ob.getA2pSource()) == a2plib.to_str('converted'): continue if ob.isSubassembly() and recursive: partListEntries = createPartList( ob.getA2pSource(), workingDir, partListEntries, recursive ) # Process information of this a2p object if not ob.isSubassembly() or not recursive: # Try to get spreadsheetdata _PARTINFO_ from linked source linkedSource1 = ob.getA2pSource() linkedSource = a2plib.findSourceFileInProject( #this returns unicode on py2 systems! linkedSource1, workingDir ) if linkedSource == None: print(u"BOM ERROR: Could not open sourcefile {}".format(linkedSource1)) continue # Is it already processed minimum one time ? entry = partListEntries.get(linkedSource,None) if entry != None: partListEntries.get(linkedSource)[0]+=1 #count sourcefile usage continue # only needed to count imports of this file, information exists yet # There is no entry in dict, need to read out information from importFile... docReader2 = FCdocumentReader() docReader2.openDocument(linkedSource) # Initialize a default parts information... partInformation = [] for i in range(0,len(PARTLIST_COLUMN_NAMES)): partInformation.append("*") # if there is a proper spreadsheet, then read it... for ob in docReader2.getSpreadsheetObjects(): sheetName = PARTINFORMATION_SHEET_NAME if a2plib.PYVERSION > 2: sheetName = a2plib.to_bytes(PARTINFORMATION_SHEET_NAME) if ob.name == sheetName: cells = ob.getCells() for addr in cells.keys(): if addr[:1] == b'B': #column B contains the data, A only the titles idx = int(addr[1:])-1 if idx < len(PARTLIST_COLUMN_NAMES): # don't read further! partInformation[idx] = cells[addr] # last entry of partinformations is reserved for filename partInformation[-1] = os.path.split(linkedSource)[1] #without complete path... # put information to dict and count usage of sourcefiles.. entry = partListEntries.get(linkedSource,None) if entry == None: partListEntries[linkedSource] = [ 1, partInformation ] else: partListEntries.get(linkedSource)[0]+=1 #count sourcefile usage return partListEntries
def updateImportedParts(doc): if doc == None: QtGui.QMessageBox.information( QtGui.QApplication.activeWindow(), "No active document found!", "Before updating parts, you have to open an assembly file.") return # modififying object's subelements causes solving of the assembly, disable autosolve here autoSolveState = a2plib.getAutoSolveState() a2plib.setAutoSolve(False) doc.openTransaction("updateImportParts") objectCache.cleanUp(doc) for obj in doc.Objects: if hasattr(obj, 'sourceFile'): if not hasattr(obj, 'timeLastImport'): obj.addProperty( "App::PropertyFloat", "timeLastImport", "importPart" ) #should default to zero which will force update. obj.setEditorMode("timeLastImport", 1) if not hasattr(obj, 'a2p_Version'): obj.addProperty("App::PropertyString", "a2p_Version", "importPart").a2p_Version = 'V0.0' obj.setEditorMode("a2p_Version", 1) if not hasattr(obj, 'muxInfo'): obj.addProperty("App::PropertyStringList", "muxInfo", "importPart").muxInfo = [] assemblyPath = os.path.normpath(os.path.split(doc.FileName)[0]) absPath = a2plib.findSourceFileInProject(obj.sourceFile, assemblyPath) if absPath == None: QtGui.QMessageBox.critical( QtGui.QApplication.activeWindow(), u"Source file not found", u"Unable to find {}".format(obj.sourceFile)) if absPath != None and os.path.exists(absPath): newPartCreationTime = os.path.getmtime(absPath) if (newPartCreationTime > obj.timeLastImport or obj.a2p_Version != A2P_VERSION): if not objectCache.isCached( absPath ): # Load every changed object one time to cache importPartFromFile(doc, absPath, importToCache=True ) # the version is now in the cache newObject = objectCache.get(absPath) obj.timeLastImport = newPartCreationTime if hasattr(newObject, 'a2p_Version'): obj.a2p_Version = A2P_VERSION importUpdateConstraintSubobjects( doc, obj, newObject) # do this before changing shape and mux if hasattr(newObject, 'muxInfo'): obj.muxInfo = newObject.muxInfo # save Placement because following newObject.Shape.copy() isn't resetting it to zeroes... savedPlacement = obj.Placement obj.Shape = newObject.Shape.copy() obj.ViewObject.DiffuseColor = copy.copy( newObject.ViewObject.DiffuseColor) obj.ViewObject.Transparency = newObject.ViewObject.Transparency obj.Placement = savedPlacement # restore the old placement mw = FreeCADGui.getMainWindow() mdi = mw.findChild(QtGui.QMdiArea) sub = mdi.activeSubWindow() if sub != None: sub.showMaximized() objectCache.cleanUp(doc) a2plib.setAutoSolve(autoSolveState) if not a2plib.getUseTopoNaming(): # This is only needed when not using toponames. # Otherwise updating constraints.subelements triggers this. a2p_solversystem.autoSolveConstraints( doc, useTransaction=False, callingFuncName="updateImportedParts" ) #transaction is already open... doc.recompute() doc.commitTransaction()