import importSVG import FreeCAD import FreeCADGui import Draft import os import Mesh from PySide.QtWidgets import QWidget, QLabel, QSpinBox, QHBoxLayout, QListWidgetItem from datetime import datetime class ObjectListItem(QWidget): def __init__(self, obj): super().__init__() self.obj = obj self.label = QLabel(obj.Label) self.spinbox = QSpinBox() self.spinbox.setRange(1, 9999) self.spinbox.setValue(1) layout = QHBoxLayout() layout.addWidget(self.label) layout.addWidget(self.spinbox) layout.setContentsMargins(0, 0, 0, 0) self.setLayout(layout) def get_value(self): return self.spinbox.value() def set_value(self, value): self.spinbox.setValue(value) class WindowDialog(): def __init__(self): self.selectedItems = [] self.outputRacine = "" self.ui_file = os.path.join(FreeCAD.getUserMacroDir(True), 'MultiExport/MultiExport.ui') self.form = FreeCADGui.PySideUic.loadUi(self.ui_file) self.connect_widgets() self.populate_selector_list() self.listVarSets() # FreeCADGui.Selection.addObserver(self.on_selection_changed) self.form.show() def connect_widgets(self): self.form.pushButtonExport.pressed.connect(self.export) self.form.pushButtonSave.pressed.connect(self.saveVarSet) self.form.pushButtonLoad.pressed.connect(self.loadVarSet) def populate_selector_list(self): self.form.listWidgetSelector.clear() FreeCADGui.Selection.clearSelection() for obj in FreeCAD.ActiveDocument.Objects: if obj.Label.endswith("_SVG") or obj.Label.endswith("_STL"): item = QListWidgetItem() widget = ObjectListItem(obj) item.setSizeHint(widget.sizeHint()) self.form.listWidgetSelector.addItem(item) self.form.listWidgetSelector.setItemWidget(item, widget) item.setSelected(True) def createFolder(self): date = datetime.today().strftime('%Y-%m-%d') time = datetime.today().strftime('%H:%M:%S') folderPath = f"{FreeCAD.activeDocument().getFileName().rpartition('.')[0]}-{date}-Exports/" if not os.path.exists(folderPath): os.makedirs(folderPath) folderPath += f"{time}/" if not os.path.exists(folderPath): os.makedirs(folderPath) return folderPath # -------- MODIFICATION DE LA MACRO "exportSketchEnMasse_SVG" de Gauthier Brière -------- # def exportSketchBasic(self, sketchList): for __O__ in sketchList: __obj__ = [] __obj__.append(__O__) print('Export SVG de : ' + __obj__[0].Label) fichierSVG = u"" + self.outputRacine + __obj__[0].Label + '-' + '.svg' importSVG.export(__obj__, fichierSVG) FreeCAD.ActiveDocument.removeObject(__O__.Label) FreeCAD.activeDocument().recompute() def exportSketchCalepinage(self, sketchList, plateLength, plateWidth, spacing): copy_sketchList = sketchList[:] __obj__ = [] currentX = 0.0 currentY = 0.0 isSVGFull = False YBlockCounter = 0 SVGNameCounter = 1 maxY = 0.0 index = 0 listSize = len(copy_sketchList) errorCount = 0 while listSize > 0: while index < listSize: __O__ = copy_sketchList[index] boundingBox = __O__.Shape.BoundBox if (boundingBox.XLength > plateLength) or (boundingBox.YLength > plateWidth): copy_sketchList.pop(index) listSize = len(copy_sketchList) errorCount += 1 FreeCAD.ActiveDocument.removeObject(__O__.Label) FreeCAD.activeDocument().recompute() continue if boundingBox.XLength + currentX > plateLength: if boundingBox.YLength + currentY > plateWidth: YBlockCounter += 1 if YBlockCounter >= listSize: isSVGFull = True break index += 1 continue if boundingBox.YLength + currentY > plateWidth: YBlockCounter += 1 if YBlockCounter >= listSize: isSVGFull = True break index += 1 continue __obj__.insert(0, __O__) maxY = boundingBox.YLength if boundingBox.YLength > maxY else maxY __obj__[0].Placement=FreeCAD.Placement(FreeCAD.Vector(0,0,0), FreeCAD.Rotation(FreeCAD.Vector(1,0,0),0), FreeCAD.Vector(0,0,0)) if currentX == 0.0: __obj__[0].Placement.Base = FreeCAD.Vector(-boundingBox.XMin + currentX, -boundingBox.YMin + currentY, 0.0) currentX += boundingBox.XLength else: __obj__[0].Placement.Base = FreeCAD.Vector(-boundingBox.XMin + currentX + spacing, -boundingBox.YMin + currentY, 0.0) currentX += boundingBox.XLength + spacing copy_sketchList.pop(index) listSize = len(copy_sketchList) FreeCAD.activeDocument().recompute() index = 0 YBlockCounter = 0 currentX = 0.0 currentY += maxY + spacing maxY = 0.0 index = 0 listSize = len(copy_sketchList) FreeCAD.activeDocument().recompute() if listSize <= 0 or isSVGFull: fichierSVG = u"" + self.outputRacine + 'CALEPINAGE-(' + str(SVGNameCounter) + ')' + '.svg' print('Export SVG de : ' + u"" + str(os.path.basename(self.outputRacine)) + 'CALEPINAGE-(' + str(SVGNameCounter) + ')' + '.svg') importSVG.export(__obj__, fichierSVG) for __i__ in __obj__: FreeCAD.ActiveDocument.removeObject(__i__.Label) FreeCAD.activeDocument().recompute() __obj__.clear() maxY = 0.0 index = 0 currentX = 0.0 currentY = 0.0 isSVGFull = False SVGNameCounter += 1 YBlockCounter = 0 listSize = len(copy_sketchList) if errorCount == 1: print(f"ERREUR : {errorCount} sketch ne rentre pas sur le plateau") elif errorCount > 1: print(f"ERREUR : {errorCount} sketchs ne rentrent pas sur le plateau") # -------- END -------- # def getLargestFaceDir(self, obj): if not hasattr(obj, "Shape") or not obj.Shape.Faces: return None maxArea = 0.0 largestFace = None for face in obj.Shape.Faces: area = face.Area if area > maxArea: maxArea = area largestFace = face if largestFace: u, v = largestFace.Surface.parameter(largestFace.CenterOfMass) normal = largestFace.normalAt(u, v) return normal.normalize() return None def exportSVG(self, isCalepinage, svgList): plateLength = self.form.doubleSpinBoxLength.value() plateWidth = self.form.doubleSpinBoxWidth.value() spacing = self.form.doubleSpinBoxSpacing.value() sketchList = [] for _, obj, objCount in svgList: for _ in range(objCount): if hasattr(obj, "Dir"): vector = FreeCAD.Vector(obj.Dir.x, obj.Dir.y, obj.Dir.z) else: vector = self.getLargestFaceDir(obj) sv0 = Draft.make_shape2dview(obj, vector) FreeCAD.ActiveDocument.recompute() sk = Draft.make_sketch(sv0, autoconstraints=True) sk.ViewObject.LineColor = (1.0, 0.0, 0.0) FreeCAD.ActiveDocument.recompute() sketchList.append(sk) if hasattr(sv0, 'Name'): FreeCAD.ActiveDocument.removeObject(sv0.Name) if isCalepinage: self.exportSketchCalepinage(sketchList, plateLength, plateWidth, spacing) else: self.exportSketchBasic(sketchList) def exportSTL(self, singleMode, stlList): objects_to_export = [] for objLabel, obj, objCount in stlList: for i in range(objCount): clone = Draft.clone(obj) objects_to_export.append((clone, objLabel, i + 1)) FreeCAD.ActiveDocument.recompute() if not singleMode: for obj, label, i in objects_to_export: filename = os.path.join(self.outputRacine, f"{label}_{i}.stl") Mesh.export([obj], filename) print(f"Exported STL: {filename}") FreeCAD.ActiveDocument.removeObject(obj.Name) else: export_objs = [obj for obj, _, _ in objects_to_export] if export_objs: label = FreeCAD.activeDocument().Name filename = os.path.join(self.outputRacine, f"{label}.stl") Mesh.export(export_objs, filename) print(f"Exported merged STL: {filename}") for obj in export_objs: FreeCAD.ActiveDocument.removeObject(obj.Name) FreeCAD.ActiveDocument.recompute() def export(self): isCalepinage = self.form.checkBoxCalepinage.isChecked() isCombine = self.form.checkBoxCombine.isChecked() svgList = [] stlList = [] for obj in FreeCAD.ActiveDocument.Objects: count = 1 for index in range(self.form.listWidgetSelector.count()): item = self.form.listWidgetSelector.item(index) widget = self.form.listWidgetSelector.itemWidget(item) if not item.isSelected(): continue if widget.obj == obj: count = widget.get_value() if obj.Label.endswith("_SVG"): svgList.append((obj.Label, obj, count)) elif obj.Label.endswith("_STL"): stlList.append((obj.Label, obj, count)) break if not svgList == [] or not stlList == []: self.outputRacine = self.createFolder() self.exportSVG(isCalepinage, svgList) self.exportSTL(isCombine, stlList) def saveVarSet(self): varSetName = self.form.saveTextValue.text() varSet = FreeCAD.ActiveDocument.addObject("App::VarSet", varSetName) self.form.saveTextValue.setText("") for obj in FreeCAD.ActiveDocument.Objects: count = 1 for index in range(self.form.listWidgetSelector.count()): item = self.form.listWidgetSelector.item(index) widget = self.form.listWidgetSelector.itemWidget(item) if widget.obj == obj: count = widget.get_value() break if obj.Label.endswith("_SVG") or obj.Label.endswith("_STL"): varSet.addProperty("App::PropertyStringList", obj.Label, "MultiExport") if obj.Label.endswith("_SVG"): setattr(varSet, obj.Label, ("SVG", str(count))) else: setattr(varSet, obj.Label, ("STL", str(count))) self.listVarSets() FreeCAD.ActiveDocument.recompute() '''def loadVarSet(self): # faire lien entre VarSet et liste # refresh a la fin ? varSetName = self.form.loadDropDown.currentText() varSet = FreeCAD.ActiveDocument.getObject(varSetName) for prop in varSet.PropertiesList: if varSet.getGroupOfProperty(prop) == "MultiExport": print(f"{prop}: {getattr(varSet, prop)}") for index in range(self.form.listWidgetSelector.count()): item = self.form.listWidgetSelector.item(index) widget = self.form.listWidgetSelector.itemWidget(item) if widget.obj == obj: count = widget.get_value() break''' def loadVarSet(self): varSetName = self.form.loadDropDown.currentText() if not varSetName: return varSet = FreeCAD.ActiveDocument.getObject(varSetName) if not varSet: return values_dict = {} for prop in varSet.PropertiesList: if varSet.getGroupOfProperty(prop) == "MultiExport": val = getattr(varSet, prop) if isinstance(val, tuple) or isinstance(val, list): try: count = int(val[1]) values_dict[prop] = count except Exception: pass for index in range(self.form.listWidgetSelector.count()): item = self.form.listWidgetSelector.item(index) widget = self.form.listWidgetSelector.itemWidget(item) if widget.obj.Label in values_dict: widget.set_value(values_dict[widget.obj.Label]) def listVarSets(self): varSetsList = [obj for obj in FreeCAD.ActiveDocument.Objects if obj.TypeId == "App::VarSet"] self.form.loadDropDown.clear() self.form.loadDropDown.setPlaceholderText("Select a VarSet") for varSet in varSetsList: self.form.loadDropDown.addItem(varSet.Label, varSet.Name) if __name__ == '__main__': try: mainWindow = WindowDialog() except Exception as e: print(e)