MultiExport-FreeCAD/MultiExport.FCMacro

393 lines
11 KiB
Plaintext
Executable file

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)