freecad-cam/Mod/BIM/ArchMaterial.py
2026-02-01 01:59:24 +01:00

900 lines
35 KiB
Python

#***************************************************************************
#* Copyright (c) 2015 Yorik van Havre <yorik@uncreated.net> *
#* *
#* This program is free software; you can redistribute it and/or modify *
#* it under the terms of the GNU Lesser General Public License (LGPL) *
#* as published by the Free Software Foundation; either version 2 of *
#* the License, or (at your option) any later version. *
#* for detail see the LICENCE text file. *
#* *
#* This program is distributed in the hope that it will be useful, *
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
#* GNU Library General Public License for more details. *
#* *
#* You should have received a copy of the GNU Library General Public *
#* License along with this program; if not, write to the Free Software *
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
#* USA *
#* *
#***************************************************************************
import FreeCAD
from draftutils import params
if FreeCAD.GuiUp:
import FreeCADGui, os
import Arch_rc # Needed for access to icons # lgtm [py/unused_import]
from PySide import QtCore, QtGui
from draftutils.translate import translate
from PySide.QtCore import QT_TRANSLATE_NOOP
else:
# \cond
def translate(ctxt,txt):
return txt
def QT_TRANSLATE_NOOP(ctxt,txt):
return txt
# \endcond
__title__ = "Arch Material Management"
__author__ = "Yorik van Havre"
__url__ = "https://www.freecad.org"
## @package ArchMaterial
# \ingroup ARCH
# \brief The Material object and tools
#
# This module provides tools to add materials to
# Arch objects
class _ArchMaterialContainer:
"The Material Container"
def __init__(self,obj):
self.Type = "MaterialContainer"
obj.Proxy = self
def execute(self,obj):
return
def dumps(self):
if hasattr(self,"Type"):
return self.Type
def loads(self,state):
if state:
self.Type = state
class _ViewProviderArchMaterialContainer:
"A View Provider for the Material Container"
def __init__(self,vobj):
vobj.Proxy = self
def getIcon(self):
return ":/icons/Arch_Material_Group.svg"
def attach(self,vobj):
self.Object = vobj.Object
def setupContextMenu(self, vobj, menu):
actionMergeByName = QtGui.QAction(QtGui.QIcon(":/icons/Arch_Material_Group.svg"),
translate("Arch", "Merge duplicates"),
menu)
actionMergeByName.triggered.connect(self.mergeByName)
menu.addAction(actionMergeByName)
actionReorder = QtGui.QAction(translate("Arch", "Reorder children alphabetically"),
menu)
actionReorder.triggered.connect(self.reorder)
menu.addAction(actionReorder)
def mergeByName(self):
if hasattr(self,"Object"):
mats = [o for o in self.Object.Group if o.isDerivedFrom("App::MaterialObject")]
todelete = []
for mat in mats:
orig = None
for om in mats:
if om.Label == mat.Label:
orig = om
break
else:
if mat.Label[-1].isdigit() and mat.Label[-2].isdigit() and mat.Label[-3].isdigit():
for om in mats:
if om.Label == mat.Label[:-3].strip():
orig = om
break
if orig:
for par in mat.InList:
for prop in par.PropertiesList:
if getattr(par,prop) == mat:
FreeCAD.Console.PrintMessage("Changed property '"+prop+"' of object "+par.Label+" from "+mat.Label+" to "+orig.Label+"\n")
setattr(par,prop,orig)
todelete.append(mat)
for tod in todelete:
if not tod.InList:
FreeCAD.Console.PrintMessage("Merging duplicate material "+tod.Label+"\n")
FreeCAD.ActiveDocument.removeObject(tod.Name)
elif (len(tod.InList) == 1) and (tod.InList[0].isDerivedFrom("App::DocumentObjectGroup")):
FreeCAD.Console.PrintMessage("Merging duplicate material "+tod.Label+"\n")
FreeCAD.ActiveDocument.removeObject(tod.Name)
else:
FreeCAD.Console.PrintMessage("Unable to delete material "+tod.Label+": InList not empty\n")
def reorder(self):
if hasattr(self,"Object"):
if hasattr(self.Object,"Group") and self.Object.Group:
g = self.Object.Group
g.sort(key=lambda obj: obj.Label)
self.Object.Group = g
FreeCAD.ActiveDocument.recompute()
def dumps(self):
return None
def loads(self,state):
return None
class _ArchMaterial:
"The Material object"
def __init__(self,obj):
self.Type = "Material"
obj.Proxy = self
self.setProperties(obj)
def onDocumentRestored(self,obj):
self.setProperties(obj)
def setProperties(self,obj):
if not "Description" in obj.PropertiesList:
obj.addProperty("App::PropertyString","Description","Material",QT_TRANSLATE_NOOP("App::Property","A description for this material"))
if not "StandardCode" in obj.PropertiesList:
obj.addProperty("App::PropertyString","StandardCode","Material",QT_TRANSLATE_NOOP("App::Property","A standard code (MasterFormat, OmniClass,...)"))
if not "ProductURL" in obj.PropertiesList:
obj.addProperty("App::PropertyString","ProductURL","Material",QT_TRANSLATE_NOOP("App::Property","A URL where to find information about this material"))
if not "Transparency" in obj.PropertiesList:
obj.addProperty("App::PropertyPercent","Transparency","Material",QT_TRANSLATE_NOOP("App::Property","The transparency value of this material"))
if not "Color" in obj.PropertiesList:
obj.addProperty("App::PropertyColor","Color","Material",QT_TRANSLATE_NOOP("App::Property","The color of this material"))
if not "SectionColor" in obj.PropertiesList:
obj.addProperty("App::PropertyColor","SectionColor","Material",QT_TRANSLATE_NOOP("App::Property","The color of this material when cut"))
def isSameColor(self,c1,c2):
r = 4
if round(c1[0],r) == round(c2[0],r):
if round(c1[1],r) == round(c2[1],r):
if round(c1[2],r) == round(c2[2],r):
return True
return False
def onChanged(self,obj,prop):
d = obj.Material
if prop == "Material":
if "SectionColor" in obj.Material:
c = tuple([float(f) for f in obj.Material['SectionColor'].strip("()").strip("[]").split(",")])
if hasattr(obj,"SectionColor"):
if not self.isSameColor(obj.SectionColor,c):
obj.SectionColor = c
if "DiffuseColor" in obj.Material:
c = tuple([float(f) for f in obj.Material['DiffuseColor'].strip("()").strip("[]").split(",")])
if hasattr(obj,"Color"):
if not self.isSameColor(obj.Color,c):
obj.Color = c
if "Transparency" in obj.Material:
t = int(obj.Material['Transparency'])
if hasattr(obj,"Transparency"):
if obj.Transparency != t:
obj.Transparency = t
if "ProductURL" in obj.Material:
if hasattr(obj,"ProductURL"):
if obj.ProductURL != obj.Material["ProductURL"]:
obj.ProductURL = obj.Material["ProductURL"]
if "StandardCode" in obj.Material:
if hasattr(obj,"StandardCode"):
if obj.StandardCode != obj.Material["StandardCode"]:
obj.StandardCode = obj.Material["StandardCode"]
if "Description" in obj.Material:
if hasattr(obj,"Description"):
if obj.Description != obj.Material["Description"]:
obj.Description = obj.Material["Description"]
if "Name" in obj.Material:
if hasattr(obj,"Label"):
if obj.Label != obj.Material["Name"]:
obj.Label = obj.Material["Name"]
elif prop == "Label":
if "Name" in d:
if d["Name"] == obj.Label:
return
d["Name"] = obj.Label
elif prop == "SectionColor":
if hasattr(obj,"SectionColor"):
if "SectionColor" in d:
if self.isSameColor(tuple([float(f) for f in d['SectionColor'].strip("()").strip("[]").split(",")]),obj.SectionColor[:3]):
return
d["SectionColor"] = str(obj.SectionColor[:3])
elif prop == "Color":
if hasattr(obj,"Color"):
if "DiffuseColor" in d:
if self.isSameColor(tuple([float(f) for f in d['DiffuseColor'].strip("()").strip("[]").split(",")]),obj.Color[:3]):
return
d["DiffuseColor"] = str(obj.Color[:3])
elif prop == "Transparency":
if hasattr(obj,"Transparency"):
val = str(obj.Transparency)
if "Transparency" in d:
if d["Transparency"] == val:
return
d["Transparency"] = val
elif prop == "ProductURL":
if hasattr(obj,"ProductURL"):
val = obj.ProductURL
if "ProductURL" in d:
if d["ProductURL"] == val:
return
obj.Material["ProductURL"] = val
elif prop == "StandardCode":
if hasattr(obj,"StandardCode"):
val = obj.StandardCode
if "StandardCode" in d:
if d["StandardCode"] == val:
return
d["StandardCode"] = val
elif prop == "Description":
if hasattr(obj,"Description"):
val = obj.Description
if "Description" in d:
if d["Description"] == val:
return
d["Description"] = val
if d and (d != obj.Material):
obj.Material = d
#if FreeCAD.GuiUp:
#import FreeCADGui
# not sure why this is needed, but it is...
#FreeCADGui.ActiveDocument.resetEdit()
def execute(self,obj):
if obj.Material:
if FreeCAD.GuiUp:
c = None
t = None
if "DiffuseColor" in obj.Material:
c = tuple([float(f) for f in obj.Material["DiffuseColor"].strip("()").strip("[]").split(",")])
if "Transparency" in obj.Material:
t = int(obj.Material["Transparency"])
for p in obj.InList:
if hasattr(p,"Material") \
and p.Material.Name == obj.Name \
and getattr(obj.ViewObject,"UseMaterialColor",True):
if c: p.ViewObject.ShapeColor = c
if t: p.ViewObject.Transparency = t
return
def dumps(self):
if hasattr(self,"Type"):
return self.Type
def loads(self,state):
if state:
self.Type = state
class _ViewProviderArchMaterial:
"A View Provider for the Material object"
def __init__(self,vobj):
vobj.Proxy = self
def getIcon(self):
if hasattr(self,"icondata"):
return self.icondata
return ":/icons/Arch_Material.svg"
def attach(self, vobj):
self.Object = vobj.Object
def updateData(self, obj, prop):
if prop == "Color":
from PySide import QtCore,QtGui
# custom icon
if hasattr(obj,"Color"):
c = obj.Color
matcolor = QtGui.QColor(int(c[0]*255),int(c[1]*255),int(c[2]*255))
darkcolor = QtGui.QColor(int(c[0]*125),int(c[1]*125),int(c[2]*125))
im = QtGui.QImage(48,48,QtGui.QImage.Format_ARGB32)
im.fill(QtCore.Qt.transparent)
pt = QtGui.QPainter(im)
pt.setPen(QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap))
#pt.setBrush(QtGui.QBrush(matcolor, QtCore.Qt.SolidPattern))
gradient = QtGui.QLinearGradient(0,0,48,48)
gradient.setColorAt(0,matcolor)
gradient.setColorAt(1,darkcolor)
pt.setBrush(QtGui.QBrush(gradient))
pt.drawEllipse(6,6,36,36)
pt.setPen(QtGui.QPen(QtCore.Qt.white, 1, QtCore.Qt.SolidLine, QtCore.Qt.FlatCap))
pt.setBrush(QtGui.QBrush(QtCore.Qt.white, QtCore.Qt.SolidPattern))
pt.drawEllipse(12,12,12,12)
pt.end()
ba = QtCore.QByteArray()
b = QtCore.QBuffer(ba)
b.open(QtCore.QIODevice.WriteOnly)
im.save(b,"XPM")
self.icondata = ba.data().decode("latin1")
def onChanged(self, vobj, prop):
if prop == "Material":
if "Father" in vobj.Object.Material:
for o in FreeCAD.ActiveDocument.Objects:
if o.isDerivedFrom("App::MaterialObject"):
if o.Label == vobj.Object.Material["Father"]:
o.touch()
def setEdit(self, vobj, mode):
if mode != 0:
return None
self.taskd = _ArchMaterialTaskPanel(vobj.Object)
FreeCADGui.Control.showDialog(self.taskd)
self.taskd.form.FieldName.setFocus()
self.taskd.form.FieldName.selectAll()
return True
def unsetEdit(self, vobj, mode):
if mode != 0:
return None
FreeCADGui.Control.closeDialog()
return True
def setupContextMenu(self, vobj, menu):
actionEdit = QtGui.QAction(translate("Arch", "Edit"),
menu)
actionEdit.triggered.connect(self.edit)
menu.addAction(actionEdit)
def edit(self):
FreeCADGui.ActiveDocument.setEdit(self.Object, 0)
def setTaskValue(self,widgetname,value):
if hasattr(self,"taskd"):
if hasattr(self.taskd,"form"):
if hasattr(self.taskd.form,widgetname):
widget = getattr(self.taskd.form,widgetname)
if hasattr(widget,"setText"):
widget.setText(value)
elif hasattr(widget,"setValue"):
widget.setText(value)
def dumps(self):
return None
def loads(self,state):
return None
def claimChildren(self):
ch = []
if hasattr(self,"Object"):
for o in self.Object.Document.Objects:
if o.isDerivedFrom("App::MaterialObject"):
if o.Material:
if "Father" in o.Material:
if o.Material["Father"] == self.Object.Label:
ch.append(o)
return ch
class _ArchMaterialTaskPanel:
'''The editmode TaskPanel for Arch Material objects'''
def __init__(self,obj=None):
self.cards = None
self.existingmaterials = []
self.obj = obj
self.form = FreeCADGui.PySideUic.loadUi(":/ui/ArchMaterial.ui")
colorPix = QtGui.QPixmap(16,16)
colorPix.fill(QtGui.QColor(204,204,204))
self.form.ButtonColor.setIcon(QtGui.QIcon(colorPix))
self.form.ButtonSectionColor.setIcon(QtGui.QIcon(colorPix))
self.form.ButtonUrl.setIcon(QtGui.QIcon(":/icons/internet-web-browser.svg"))
self.form.comboBox_MaterialsInDir.currentIndexChanged.connect(self.chooseMat)
self.form.comboBox_FromExisting.currentIndexChanged.connect(self.fromExisting)
self.form.comboFather.currentTextChanged.connect(self.setFather)
self.form.ButtonColor.pressed.connect(self.getColor)
self.form.ButtonSectionColor.pressed.connect(self.getSectionColor)
self.form.ButtonUrl.pressed.connect(self.openUrl)
self.form.ButtonEditor.pressed.connect(self.openEditor)
self.form.ButtonCode.pressed.connect(self.getCode)
self.fillMaterialCombo()
self.fillExistingCombo()
try:
from bimcommands import BimClassification
except Exception:
self.form.ButtonCode.hide()
else:
self.form.ButtonCode.setIcon(QtGui.QIcon(":/icons/BIM_Classification.svg"))
if self.obj:
if hasattr(self.obj,"Material"):
self.material = self.obj.Material
self.setFields()
def setFields(self):
"sets the task box contents from self.material"
if 'Name' in self.material:
self.form.FieldName.setText(self.material['Name'])
elif self.obj:
self.form.FieldName.setText(self.obj.Label)
if 'Description' in self.material:
self.form.FieldDescription.setText(self.material['Description'])
if 'DiffuseColor' in self.material:
self.form.ButtonColor.setIcon(self.getColorIcon(self.material["DiffuseColor"]))
elif 'ViewColor' in self.material:
self.form.ButtonColor.setIcon(self.getColorIcon(self.material["ViewColor"]))
elif 'Color' in self.material:
self.form.ButtonColor.setIcon(self.getColorIcon(self.material["Color"]))
if 'SectionColor' in self.material:
self.form.ButtonSectionColor.setIcon(self.getColorIcon(self.material["SectionColor"]))
if 'StandardCode' in self.material:
self.form.FieldCode.setText(self.material['StandardCode'])
if 'ProductURL' in self.material:
self.form.FieldUrl.setText(self.material['ProductURL'])
if 'Transparency' in self.material:
self.form.SpinBox_Transparency.setValue(int(self.material["Transparency"]))
if "Father" in self.material:
father = self.material["Father"]
else:
father = None
found = False
self.form.comboFather.addItem("None")
for o in FreeCAD.ActiveDocument.Objects:
if o.isDerivedFrom("App::MaterialObject"):
if o != self.obj:
self.form.comboFather.addItem(o.Label)
if o.Label == father:
self.form.comboFather.setCurrentIndex(self.form.comboFather.count()-1)
found = True
if father and not found:
self.form.comboFather.addItem(father)
self.form.comboFather.setCurrentIndex(self.form.comboFather.count()-1)
def getColorIcon(self,color):
if color:
if "(" in color:
c = tuple([float(f) for f in color.strip("()").split(",")])
qcolor = QtGui.QColor()
qcolor.setRgbF(c[0],c[1],c[2])
colorPix = QtGui.QPixmap(16,16)
colorPix.fill(qcolor)
icon = QtGui.QIcon(colorPix)
return icon
return QtGui.QIcon()
def getFields(self):
"sets self.material from the contents of the task box"
self.material['Name'] = self.form.FieldName.text()
self.material['Description'] = self.form.FieldDescription.text()
self.material['DiffuseColor'] = self.getColorFromIcon(self.form.ButtonColor.icon())
self.material['ViewColor'] = self.material['DiffuseColor']
self.material['Color'] = self.material['DiffuseColor']
self.material['SectionColor'] = self.getColorFromIcon(self.form.ButtonSectionColor.icon())
self.material['StandardCode'] = self.form.FieldCode.text()
self.material['ProductURL'] = self.form.FieldUrl.text()
self.material['Transparency'] = str(self.form.SpinBox_Transparency.value())
def getColorFromIcon(self,icon):
"gets pixel color from the given icon"
pixel = icon.pixmap(16,16).toImage().pixel(0,0)
return str(QtGui.QColor(pixel).getRgbF())
def accept(self):
self.getFields()
if self.obj:
if hasattr(self.obj,"Material"):
self.obj.Material = self.material
self.obj.Label = self.material['Name']
FreeCAD.ActiveDocument.recompute()
FreeCADGui.ActiveDocument.resetEdit()
return True
def reject(self):
FreeCADGui.ActiveDocument.resetEdit()
return True
def chooseMat(self, card):
"sets self.material from a card"
card = self.form.comboBox_MaterialsInDir.currentText()
if card in self.cards:
import importFCMat
self.material = importFCMat.read(self.cards[card])
self.setFields()
def fromExisting(self,index):
"sets the contents from an existing material"
if index > 0:
if index <= len(self.existingmaterials):
m = self.existingmaterials[index-1]
if m.Material:
self.material = m.Material
self.setFields()
def setFather(self, text):
"sets the father"
if text:
if text == "None":
if "Father" in self.material:
# for some have Father at first and change to none
self.material.pop("Father")
else:
self.material["Father"] = text
def getColor(self):
self.getColorForButton(self.form.ButtonColor)
def getSectionColor(self):
self.getColorForButton(self.form.ButtonSectionColor)
def getColorForButton(self,button):
"opens a color picker dialog"
icon = button.icon()
pixel = icon.pixmap(16,16).toImage().pixel(0,0)
color = QtGui.QColorDialog.getColor(QtGui.QColor(pixel))
if color.isValid():
colorPix = QtGui.QPixmap(16,16)
colorPix.fill(color)
button.setIcon(QtGui.QIcon(colorPix))
def fillMaterialCombo(self):
"fills the combo with the existing FCMat cards"
# look for cards in both resources dir and a Materials sub-folder in the user folder.
# User cards with same name will override system cards
resources_mat_path = os.path.join(FreeCAD.getResourceDir(), "Mod", "Material", "Resources", "Materials")
resources_mat_path_std = os.path.join(resources_mat_path, "Standard")
user_mat_path = os.path.join(FreeCAD.ConfigGet("UserAppData"), "Material")
paths = [resources_mat_path_std]
if os.path.exists(user_mat_path):
paths.append(user_mat_path)
self.cards = {}
for p in paths:
for root, _, f_names in os.walk(p):
for f in f_names:
b,e = os.path.splitext(f)
if e.upper() == ".FCMAT":
self.cards[b] = os.path.join(root, f)
if self.cards:
for k in sorted(self.cards):
self.form.comboBox_MaterialsInDir.addItem(k)
def fillExistingCombo(self):
"fills the existing materials combo"
self.existingmaterials = []
for obj in FreeCAD.ActiveDocument.Objects:
if obj.isDerivedFrom("App::MaterialObject"):
if obj != self.obj:
self.existingmaterials.append(obj)
for m in self.existingmaterials:
self.form.comboBox_FromExisting.addItem(m.Label)
def openEditor(self):
"opens the full material editor from the material module"
self.getFields()
if self.material:
import MaterialEditor
self.material = MaterialEditor.editMaterial(self.material)
self.setFields()
def openUrl(self):
self.getFields()
if self.material:
if 'ProductURL' in self.material:
QtGui.QDesktopServices.openUrl(self.material['ProductURL'])
def getCode(self):
FreeCADGui.Selection.addSelection(self.obj)
FreeCADGui.runCommand("BIM_Classification")
class _ArchMultiMaterial:
"The MultiMaterial object"
def __init__(self,obj):
self.Type = "MultiMaterial"
obj.Proxy = self
obj.addProperty("App::PropertyString","Description","Arch",QT_TRANSLATE_NOOP("App::Property","A description for this material"))
obj.addProperty("App::PropertyStringList","Names","Arch",QT_TRANSLATE_NOOP("App::Property","The list of layer names"))
obj.addProperty("App::PropertyLinkList","Materials","Arch",QT_TRANSLATE_NOOP("App::Property","The list of layer materials"))
obj.addProperty("App::PropertyFloatList","Thicknesses","Arch",QT_TRANSLATE_NOOP("App::Property","The list of layer thicknesses"))
def dumps(self):
if hasattr(self,"Type"):
return self.Type
def loads(self,state):
if state:
self.Type = state
class _ViewProviderArchMultiMaterial:
"A View Provider for the MultiMaterial object"
def __init__(self,vobj):
vobj.Proxy = self
def getIcon(self):
return ":/icons/Arch_Material_Multi.svg"
def attach(self, vobj):
self.Object = vobj.Object
def setEdit(self, vobj, mode):
if mode != 0:
return None
taskd = _ArchMultiMaterialTaskPanel(vobj.Object)
FreeCADGui.Control.showDialog(taskd)
return True
def unsetEdit(self, vobj, mode):
if mode != 0:
return None
FreeCADGui.Control.closeDialog()
return True
def doubleClicked(self,vobj):
self.edit()
def setupContextMenu(self, vobj, menu):
actionEdit = QtGui.QAction(translate("Arch", "Edit"),
menu)
actionEdit.triggered.connect(self.edit)
menu.addAction(actionEdit)
def edit(self):
FreeCADGui.ActiveDocument.setEdit(self.Object, 0)
def dumps(self):
return None
def loads(self,state):
return None
def isShow(self):
return True
if FreeCAD.GuiUp:
class MultiMaterialDelegate(QtGui.QStyledItemDelegate):
def __init__(self, parent=None, *args):
self.mats = []
for obj in FreeCAD.ActiveDocument.Objects:
if obj.isDerivedFrom("App::MaterialObject"):
self.mats.append(obj)
QtGui.QStyledItemDelegate.__init__(self, parent, *args)
def createEditor(self,parent,option,index):
if index.column() == 0:
editor = QtGui.QComboBox(parent)
editor.setEditable(True)
elif index.column() == 1:
editor = QtGui.QComboBox(parent)
elif index.column() == 2:
ui = FreeCADGui.UiLoader()
editor = ui.createWidget("Gui::InputField")
editor.setSizePolicy(QtGui.QSizePolicy.Preferred,QtGui.QSizePolicy.Minimum)
editor.setParent(parent)
else:
editor = QtGui.QLineEdit(parent)
return editor
def setEditorData(self, editor, index):
if index.column() == 0:
import ArchWindow
editor.addItems([index.data()]+ArchWindow.WindowPartTypes)
elif index.column() == 1:
idx = -1
for i,m in enumerate(self.mats):
editor.addItem(m.Label)
if m.Label == index.data():
idx = i
editor.setCurrentIndex(idx)
else:
QtGui.QStyledItemDelegate.setEditorData(self, editor, index)
def setModelData(self, editor, model, index):
if index.column() == 0:
if editor.currentIndex() == -1:
model.setData(index, "")
else:
model.setData(index, editor.currentText())
elif index.column() == 1:
if editor.currentIndex() == -1:
model.setData(index, "")
else:
model.setData(index, self.mats[editor.currentIndex()].Label)
else:
QtGui.QStyledItemDelegate.setModelData(self, editor, model, index)
class _ArchMultiMaterialTaskPanel:
'''The editmode TaskPanel for MultiMaterial objects'''
def __init__(self,obj=None):
self.obj = obj
self.form = FreeCADGui.PySideUic.loadUi(":/ui/ArchMultiMaterial.ui")
self.model = QtGui.QStandardItemModel()
self.model.setHorizontalHeaderLabels([translate("Arch","Name"),translate("Arch","Material"),translate("Arch","Thickness")])
self.form.tree.setModel(self.model)
self.form.tree.setUniformRowHeights(True)
self.form.tree.setItemDelegate(MultiMaterialDelegate())
self.form.chooseCombo.currentIndexChanged.connect(self.fromExisting)
self.form.addButton.pressed.connect(self.addLayer)
self.form.upButton.pressed.connect(self.upLayer)
self.form.downButton.pressed.connect(self.downLayer)
self.form.delButton.pressed.connect(self.delLayer)
self.form.invertButton.pressed.connect(self.invertLayer)
self.model.itemChanged.connect(self.recalcThickness)
self.fillExistingCombo()
self.fillData()
def fillData(self,obj=None):
if not obj:
obj = self.obj
if obj:
self.model.clear()
self.model.setHorizontalHeaderLabels([translate("Arch","Name"),translate("Arch","Material"),translate("Arch","Thickness")])
# restore widths
self.form.tree.setColumnWidth(0,params.get_param_arch("MultiMaterialColumnWidth0"))
self.form.tree.setColumnWidth(1,params.get_param_arch("MultiMaterialColumnWidth1"))
for i in range(len(obj.Names)):
item1 = QtGui.QStandardItem(obj.Names[i])
item2 = QtGui.QStandardItem(obj.Materials[i].Label)
item3 = QtGui.QStandardItem(FreeCAD.Units.Quantity(obj.Thicknesses[i],FreeCAD.Units.Length).getUserPreferred()[0])
self.model.appendRow([item1,item2,item3])
self.form.nameField.setText(obj.Label)
def fillExistingCombo(self):
"fills the existing multimaterials combo"
import Draft
self.existingmaterials = []
for obj in FreeCAD.ActiveDocument.Objects:
if Draft.getType(obj) == "MultiMaterial":
if obj != self.obj:
self.existingmaterials.append(obj)
for m in self.existingmaterials:
self.form.chooseCombo.addItem(m.Label)
def fromExisting(self,index):
"sets the contents from an existing material"
if index > 0:
if index <= len(self.existingmaterials):
m = self.existingmaterials[index-1]
if m:
self.fillData(m)
def addLayer(self):
item1 = QtGui.QStandardItem(translate("Arch","New layer"))
item2 = QtGui.QStandardItem()
item3 = QtGui.QStandardItem()
self.model.appendRow([item1,item2,item3])
def delLayer(self):
sel = self.form.tree.selectedIndexes()
if sel:
row = sel[0].row()
if row >= 0:
self.model.takeRow(row)
self.recalcThickness()
def moveLayer(self,mvt=0):
sel = self.form.tree.selectedIndexes()
if sel and mvt:
row = sel[0].row()
if row >= 0:
if row+mvt >= 0:
data = self.model.takeRow(row)
self.model.insertRow(row+mvt,data)
ind = self.model.index(row+mvt,0)
self.form.tree.setCurrentIndex(ind)
def upLayer(self):
self.moveLayer(mvt=-1)
def downLayer(self):
self.moveLayer(mvt=1)
def invertLayer(self):
items = [self.model.takeRow(row) for row in range(self.model.rowCount()-1,-1,-1)]
items.reverse()
for item in items:
self.model.insertRow(0,item)
def recalcThickness(self,item=None):
prefix = translate("Arch","Total thickness")+": "
th = 0
suffix = ""
for row in range(self.model.rowCount()):
thick = 0
d = self.model.item(row,2).text()
try:
d = float(d)
except Exception:
thick = FreeCAD.Units.Quantity(d).Value
else:
thick = FreeCAD.Units.Quantity(d,FreeCAD.Units.Length).Value
th += abs(thick)
if not thick:
suffix = " ("+translate("Arch","depends on the object")+")"
val = FreeCAD.Units.Quantity(th,FreeCAD.Units.Length).UserString
self.form.labelTotalThickness.setText(prefix + val + suffix)
def accept(self):
# store widths
params.set_param_arch("MultiMaterialColumnWidth0",self.form.tree.columnWidth(0))
params.set_param_arch("MultiMaterialColumnWidth1",self.form.tree.columnWidth(1))
if self.obj:
mats = []
for m in FreeCAD.ActiveDocument.Objects:
if m.isDerivedFrom("App::MaterialObject"):
mats.append(m)
names = []
materials = []
thicknesses = []
for row in range(self.model.rowCount()):
name = self.model.item(row,0).text()
mat = None
ml = self.model.item(row,1).text()
for m in mats:
if m.Label == ml:
mat = m
d = self.model.item(row,2).text()
try:
d = float(d)
except Exception:
thick = FreeCAD.Units.Quantity(d).Value
else:
thick = FreeCAD.Units.Quantity(d,FreeCAD.Units.Length).Value
if round(thick,32) == 0:
thick = 0.0
if name and mat:
names.append(name)
materials.append(mat)
thicknesses.append(thick)
self.obj.Names = names
self.obj.Materials = materials
self.obj.Thicknesses = thicknesses
if self.form.nameField.text():
self.obj.Label = self.form.nameField.text()
FreeCAD.ActiveDocument.recompute()
FreeCADGui.ActiveDocument.resetEdit()
return True
def reject(self):
FreeCADGui.ActiveDocument.resetEdit()
return True