From b63643b1530f9585e1eee8eaa31e50f206a25f7d Mon Sep 17 00:00:00 2001 From: Youen Date: Mon, 1 May 2023 16:09:25 +0200 Subject: [PATCH] Added system to export part list in CSV file Added possibility to set material and/or weight of each part Part weight is calculated from volume and density if the material is configured --- InitGui.py | 3 + ahb_cmd_export_parts_list.py | 106 +++++++++++++++++++++++++++++++++++ ahb_material.py | 22 ++++++++ ahb_techdraw_extensions.py | 21 +++++++ 4 files changed, 152 insertions(+) create mode 100644 ahb_cmd_export_parts_list.py create mode 100644 ahb_material.py diff --git a/InitGui.py b/InitGui.py index 263872b..c7f69aa 100644 --- a/InitGui.py +++ b/InitGui.py @@ -103,6 +103,9 @@ class AssemblyHandbookWorkbench(Gui.Workbench): self.importModule('ahb_cmd_view_refresh') toolbox.append("AHB_view_refresh") + + self.importModule('ahb_cmd_export_parts_list') + toolbox.append("AHB_export_parts_list") if self.dev: self.importModule('ahb_cmd_reload') diff --git a/ahb_cmd_export_parts_list.py b/ahb_cmd_export_parts_list.py new file mode 100644 index 0000000..7eb675d --- /dev/null +++ b/ahb_cmd_export_parts_list.py @@ -0,0 +1,106 @@ +import FreeCADGui as Gui +import FreeCAD as App + +import ahb_utils +from ahb_material import Material + +class PartInfo: + def __init__(self, document, obj): + self.document = document + self.reference = obj.Label + + if len(self.reference) == 3 and self.reference[0:1] in ['L', 'M', 'T', 'R', 'E']: + self.reference = 'TB_' + self.reference + + self.material = 'Unknown' + try: + self.material = obj.Assembly_handbook_Material + except: + pass + + self.size = [obj.Shape.BoundBox.XLength, obj.Shape.BoundBox.YLength, obj.Shape.BoundBox.ZLength] # in mm + self.volume = obj.Shape.Volume # in mm3 + self.mass = -1 # in g (negative means unknown mass) + density = -1 # in g/cm3 + material = Material.Get(self.material) + if material is not None: + density = material.density + if density >= 0: self.mass = density * (self.volume / 1000) + try: + part_mass = obj.Assembly_handbook_Weight + if part_mass >= 0: + self.mass = part_mass + except: + pass + self.count = 0 + +class AHB_ExportPartsList: + def GetResources(self): + return {"MenuText": "Export parts list (CSV)", + "ToolTip": "Exports all parts of the selected assembly in CSV format", + "Pixmap": "" + } + + def IsActive(self): + obj = Gui.Selection.getSelection() + if len(obj) == 1: + obj = obj[0] + return obj.TypeId == 'App::Part' and 'AssemblyType' in obj.PropertiesList + return False + + def Activated(self): + all_parts = {} + + rootAssembly = Gui.Selection.getSelection()[0] + + def add_part(part): + #print(part.Label + " (" + part.LinkedObject.Label + ")") + try: + info = all_parts[part.LinkedObject] + except: + info = PartInfo(part.LinkedObject.Document.Name, part.LinkedObject) + all_parts[part.LinkedObject] = info + + info.count += 1 + + def process_group(group): + for part in group.Group: + if not part.Visibility: + continue + + isPartLink = part.TypeId == 'App::Link' or (part.TypeId == 'Part::FeaturePython' and hasattr(part, 'LinkedObject')) + partType = None + isSinglePart = None + try: + partType = part.Type + isSinglePart = part.Assembly_handbook_IsSinglePart + except: + pass + + if isPartLink: + if isSinglePart != True and (partType == 'Assembly' or (part.Group[1].Label == 'Constraints' and part.Group[2].Label == 'Variables' and part.Group[3].Label == 'Configurations')): + process_group(part.LinkedObject) + else: + add_part(part) + else: + if part.TypeId != 'App::DocumentObjectGroup' and part.TypeId != 'Spreadsheet::Sheet' and partType != 'App::PropertyContainer': + print("??? " + part.Label) + pass + + print("Exporting all parts contained in assembly: " + rootAssembly.Label + "...") + process_group(rootAssembly) + + file_name = rootAssembly.Document.FileName.replace('.FCStd', '') + '_list.csv' + with open(file_name, "w") as f: + f.write("Document, Reference, Material, SizeX (mm), SizeY (mm), SizeZ (mm), Volume (cm3), Mass (g), Count\n") + for part in all_parts.values(): + mass_str = '' + if part.mass >= 10: + mass_str = str(int(part.mass + 0.5)) + elif part.mass >= 0: + mass_str = str(int(part.mass*10 + 0.5) / 10.0) + f.write(part.document + ", " + part.reference + "," + part.material + "," + str(int(part.size[0]+0.5)) + "," + str(int(part.size[1]+0.5)) + "," + str(int(part.size[2]+0.5)) + "," + str(int(part.volume/100+0.5)/10.0) + "," + mass_str + ", " + str(part.count) + "\n") + print("Part list exported to " + file_name) + +from ahb_command import AHB_CommandWrapper +AHB_CommandWrapper.addGuiCommand('AHB_export_parts_list', AHB_ExportPartsList()) diff --git a/ahb_material.py b/ahb_material.py new file mode 100644 index 0000000..0938a07 --- /dev/null +++ b/ahb_material.py @@ -0,0 +1,22 @@ +class Material: + def __init__(self, ID, density): + self.ID = ID + self.density = density + + DB = [] + + @staticmethod + def GetMaterialIDs(): + result = [] + for m in Material.DB: + result.append(m.ID) + return result + + @staticmethod + def Get(ID): + for m in Material.DB: + if m.ID == ID: return m + return None + +Material.DB.append(Material('Stainless steel', density = 8.00)) +Material.DB.append(Material('Aluminium', density = 2.71)) diff --git a/ahb_techdraw_extensions.py b/ahb_techdraw_extensions.py index 0f368c3..0aec94f 100644 --- a/ahb_techdraw_extensions.py +++ b/ahb_techdraw_extensions.py @@ -7,6 +7,8 @@ import TechDraw, TechDrawGui from PySide import QtGui, QtCore TDG = TechDrawGui +from ahb_material import Material + class CursorItem(QtGui.QGraphicsItem): def __init__(self, parent = None, view = None): super().__init__(parent) @@ -792,13 +794,32 @@ class TechDrawExtensions: def initializeDocument(self, doc): def doInit(): + main_part = None try: for obj in doc.Objects: if obj.TypeId == 'TechDraw::DrawPage': self.onPageLoaded(obj) + + main_part = doc.getObjectsByLabel(doc.Name) + if len(main_part) == 1: + main_part = main_part[0] except: pass + if main_part is not None: + current_material = 'Unknown' + if 'Assembly_handbook_Material' in main_part.PropertiesList: + current_material = main_part.Assembly_handbook_Material + else: + main_part.addProperty("App::PropertyEnumeration", "Assembly_handbook_Material", "Assembly_handbook") + material_list = ['Unknown'] + Material.GetMaterialIDs() + main_part.Assembly_handbook_Material = material_list + main_part.Assembly_handbook_Material = material_list.index(current_material) if current_material in material_list else 0 + + if 'Assembly_handbook_Weight' not in main_part.PropertiesList: + main_part.addProperty("App::PropertyFloat", "Assembly_handbook_Weight", "Assembly_handbook", 'Part weight in grams. Set a negative number if weight is unknown.') + main_part.Assembly_handbook_Weight = -1 + QTimer.singleShot(0, doInit) def onPageLoaded(self, page):