Merge pull request #2094 from berndhahnebach/bhbdev210

FEM and Material: small fixes and code formating
This commit is contained in:
Bernd Hahnebach
2019-04-23 11:26:16 +02:00
committed by GitHub
5 changed files with 482 additions and 224 deletions

View File

@@ -1,6 +1,6 @@
# ***************************************************************************
# * *
# * Copyright (c) 2013 Juergen Riegel <FreeCAD@juergen-riegel.net> *
# * Copyright (c) 2013 Juergen Riegel <FreeCAD@juergen-riegel.net> *
# * Copyright (c) 2016 Bernd Hahnebach <bernd@bimstatik.org> *
# * *
# * This program is free software; you can redistribute it and/or modify *
# * it under the terms of the GNU Lesser General Public License (LGPL) *
@@ -77,10 +77,12 @@ class _ViewProviderFemMaterial:
FreeCADGui.Control.closeDialog()
return True
# overwrite the doubleClicked of material object python to make sure no other Material taskd (and thus no selection observer) is still active
# overwrite the doubleClicked of material object python to make sure no other Material taskd
# (and thus no selection observer) is still active
def doubleClicked(self, vobj):
guidoc = FreeCADGui.getDocument(vobj.Object.Document)
# check if another VP is in edit mode, https://forum.freecadweb.org/viewtopic.php?t=13077#p104702
# check if another VP is in edit mode
# https://forum.freecadweb.org/viewtopic.php?t=13077#p104702
if not guidoc.getInEdit():
guidoc.setEdit(vobj.Object.Name)
else:
@@ -106,31 +108,79 @@ class _TaskPanelFemMaterial:
self.material = self.obj.Material # FreeCAD material dictionary of current material
self.card_path = ''
self.materials = {} # { card_path : FreeCAD material dict }
self.icons = {} # { card_path : icon_path }
# mat_card is the FCMat file
# card_name is the file name of the mat_card
# card_path is the whole file path of the mat_card
# material_name is the value of the key name in FreeCAD material dictionary
# they might not match because of special letters in the material_name which are changed in the card_name to english standard characters
# they might not match because of special letters in the material_name
# which are changed in the card_name to english standard characters
self.has_transient_mat = False
# parameter widget
self.parameterWidget = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/Material.ui")
self.parameterWidget = FreeCADGui.PySideUic.loadUi(
FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/Material.ui"
)
# globals
QtCore.QObject.connect(self.parameterWidget.cb_materials, QtCore.SIGNAL("activated(int)"), self.choose_material)
QtCore.QObject.connect(self.parameterWidget.chbu_allow_edit, QtCore.SIGNAL("clicked()"), self.toggleInputFieldsReadOnly)
QtCore.QObject.connect(self.parameterWidget.pushButton_editMat, QtCore.SIGNAL("clicked()"), self.edit_material)
QtCore.QObject.connect(
self.parameterWidget.cb_materials,
QtCore.SIGNAL("activated(int)"),
self.choose_material
)
QtCore.QObject.connect(
self.parameterWidget.chbu_allow_edit,
QtCore.SIGNAL("clicked()"),
self.toggleInputFieldsReadOnly
)
QtCore.QObject.connect(
self.parameterWidget.pushButton_editMat,
QtCore.SIGNAL("clicked()"),
self.edit_material
)
# basic properties must be provided
QtCore.QObject.connect(self.parameterWidget.input_fd_density, QtCore.SIGNAL("editingFinished()"), self.density_changed)
QtCore.QObject.connect(
self.parameterWidget.input_fd_density,
QtCore.SIGNAL("editingFinished()"),
self.density_changed
)
# mechanical properties
QtCore.QObject.connect(self.parameterWidget.input_fd_young_modulus, QtCore.SIGNAL("editingFinished()"), self.ym_changed)
QtCore.QObject.connect(self.parameterWidget.spinBox_poisson_ratio, QtCore.SIGNAL("editingFinished()"), self.pr_changed)
QtCore.QObject.connect(
self.parameterWidget.input_fd_young_modulus,
QtCore.SIGNAL("editingFinished()"),
self.ym_changed
)
QtCore.QObject.connect(
self.parameterWidget.spinBox_poisson_ratio,
QtCore.SIGNAL("editingFinished()"),
self.pr_changed
)
# thermal properties
QtCore.QObject.connect(self.parameterWidget.input_fd_thermal_conductivity, QtCore.SIGNAL("editingFinished()"), self.tc_changed)
QtCore.QObject.connect(self.parameterWidget.input_fd_expansion_coefficient, QtCore.SIGNAL("editingFinished()"), self.tec_changed)
QtCore.QObject.connect(self.parameterWidget.input_fd_specific_heat, QtCore.SIGNAL("editingFinished()"), self.sh_changed)
QtCore.QObject.connect(
self.parameterWidget.input_fd_thermal_conductivity,
QtCore.SIGNAL("editingFinished()"),
self.tc_changed
)
QtCore.QObject.connect(
self.parameterWidget.input_fd_expansion_coefficient,
QtCore.SIGNAL("editingFinished()"),
self.tec_changed
)
QtCore.QObject.connect(
self.parameterWidget.input_fd_specific_heat,
QtCore.SIGNAL("editingFinished()"),
self.sh_changed
)
# fluidic properties, only volumetric thermal expansion coeff makes sense
QtCore.QObject.connect(self.parameterWidget.input_fd_kinematic_viscosity, QtCore.SIGNAL("editingFinished()"), self.kinematic_viscosity_changed)
QtCore.QObject.connect(self.parameterWidget.input_fd_vol_expansion_coefficient, QtCore.SIGNAL("editingFinished()"), self.vtec_changed)
QtCore.QObject.connect(
self.parameterWidget.input_fd_kinematic_viscosity,
QtCore.SIGNAL("editingFinished()"),
self.kinematic_viscosity_changed
)
QtCore.QObject.connect(
self.parameterWidget.input_fd_vol_expansion_coefficient,
QtCore.SIGNAL("editingFinished()"),
self.vtec_changed
)
# init all parameter input files with read only
self.parameterWidget.chbu_allow_edit.setCheckState(QtCore.Qt.CheckState.Unchecked)
@@ -147,8 +197,10 @@ class _TaskPanelFemMaterial:
self.parameterWidget.label_vol_expansion_coefficient.setVisible(0)
self.parameterWidget.input_fd_vol_expansion_coefficient.setVisible(0)
# fill self.materials dict and fill the combobox with material cards
# get all available materials (fill self.materials and self.icons)
self.import_materials()
# fill the material comboboxes with material cards
self.add_cards_to_combo_box()
# search for exact this mat_card in all known cards, choose the current material
self.card_path = self.get_material_card(self.material)
@@ -156,22 +208,38 @@ class _TaskPanelFemMaterial:
if not self.card_path:
# we have not found our material in self.materials dict :-(
# we're going to add a user-defined temporary material: a document material
FreeCAD.Console.PrintMessage("Previously used material card cannot be found in material directories. Add document material.\n")
FreeCAD.Console.PrintMessage(
"Previously used material card cannot be found in material directories. "
"Add document material.\n"
)
self.card_path = '_document_material'
self.materials[self.card_path] = self.material
self.parameterWidget.cb_materials.addItem(QtGui.QIcon(":/icons/help-browser.svg"), self.card_path, self.card_path)
self.parameterWidget.cb_materials.addItem(
QtGui.QIcon(":/icons/help-browser.svg"),
self.card_path,
self.card_path
)
index = self.parameterWidget.cb_materials.findData(self.card_path)
# print(index)
self.choose_material(index) # fill input fields and set the current material in the cb widget
# fill input fields and set the current material in the cb widget
self.choose_material(index)
else:
# we found our exact material in self.materials dict :-)
FreeCAD.Console.PrintMessage("Previously used material card was found in material directories. We will use this material.\n")
FreeCAD.Console.PrintMessage(
"Previously used material card was found in material directories. "
"We will use this material.\n"
)
index = self.parameterWidget.cb_materials.findData(self.card_path)
# print(index)
self.choose_material(index) # fill input fields and set the current material in the cb widget
# fill input fields and set the current material in the cb widget
self.choose_material(index)
# geometry selection widget
self.selectionWidget = FemSelectionWidgets.GeometryElementsSelection(obj.References, ['Solid', 'Face', 'Edge'], False) # start with Solid in list!
self.selectionWidget = FemSelectionWidgets.GeometryElementsSelection(
obj.References,
['Solid', 'Face', 'Edge'],
False
) # start with Solid in list!
# form made from param and selection widget
self.form = [self.parameterWidget, self.selectionWidget]
@@ -179,10 +247,11 @@ class _TaskPanelFemMaterial:
# check references, has to be after initialisation of selectionWidget
self.selectionWidget.has_equal_references_shape_types()
# leave task panel **********************************************
# leave task panel ***************************************************************************
def accept(self):
# print(self.material)
if self.selectionWidget.has_equal_references_shape_types():
self.do_not_set_thermal_zeros()
self.obj.Material = self.material
self.obj.References = self.selectionWidget.references
self.recompute_and_set_back_all()
@@ -200,11 +269,35 @@ class _TaskPanelFemMaterial:
FreeCADGui.Selection.removeObserver(self.selectionWidget.sel_server)
doc.resetEdit()
# choose material ***********************************************
def do_not_set_thermal_zeros(self):
''' thermal material parameter are set to 0.0 if not available
this leads to wrong material values and to not finding the card
on reopen the task pane, thus do not write thermal parameter,
if they are 0.0
'''
if Units.Quantity(self.material['ThermalConductivity']) == 0.0:
self.material.pop('ThermalConductivity', None)
FreeCAD.Console.PrintMessage(
'Zero ThermalConductivity value. It is not saved in the card data.\n'
)
if Units.Quantity(self.material['ThermalExpansionCoefficient']) == 0.0:
self.material.pop('ThermalExpansionCoefficient', None)
FreeCAD.Console.PrintMessage(
'Zero ThermalExpansionCoefficient value. It is not saved in the card data.\n'
)
if Units.Quantity(self.material['SpecificHeat']) == 0.0:
self.material.pop('SpecificHeat', None)
FreeCAD.Console.PrintMessage(
'Zero SpecificHeat value. It is not saved in the card data.\n'
)
# choose material ****************************************************************************
def get_material_card(self, material):
for a_mat in self.materials:
unmatched_items = set(self.materials[a_mat].items()) ^ set(material.items())
# print(a_mat + ' --> unmatched_items = ' + str(len(unmatched_items)))
# if len(unmatched_items) < 4:
# print(unmatched_items)
if len(unmatched_items) == 0:
return a_mat
return ""
@@ -212,7 +305,7 @@ class _TaskPanelFemMaterial:
def choose_material(self, index):
if index < 0:
return
self.card_path = self.parameterWidget.cb_materials.itemData(index) # returns the whole path !
self.card_path = self.parameterWidget.cb_materials.itemData(index) # returns whole path
# print('choose_material: ' + self.card_path)
self.material = self.materials[self.card_path]
self.check_material_keys()
@@ -237,28 +330,52 @@ class _TaskPanelFemMaterial:
def add_transient_material(self):
self.has_transient_mat = True
self.card_path = '_transient_material'
self.parameterWidget.cb_materials.addItem(QtGui.QIcon(":/icons/help-browser.svg"), self.card_path, self.card_path)
self.parameterWidget.cb_materials.addItem(
QtGui.QIcon(":/icons/help-browser.svg"),
self.card_path,
self.card_path
)
self.set_transient_material()
# how to edit a material ****************************************
# how to edit a material *********************************************************************
def edit_material(self):
# opens the material editor to choose a material or edit material params
# self.print_material_params()
import MaterialEditor
new_material_params = self.material.copy()
new_material_params = MaterialEditor.editMaterial(new_material_params)
# if the material editor was canceled a empty params dict will be returned, do not change the self.material
# if the material editor was canceled a empty params dict will be returned
# do not change the self.material
# self.print_material_params(new_material_params)
if new_material_params: # returns True if dict is not empty (do not use 'is True', this would return False for a non empty dict)
# check if dict is not empty (do not use 'is True'
if new_material_params:
self.material = new_material_params
self.check_material_keys()
self.set_mat_params_in_input_fields(self.material)
if self.has_transient_mat is False:
self.add_transient_material()
self.card_path = self.get_material_card(self.material)
# print('card_path: ' + self.card_path)
self.check_material_keys()
self.set_mat_params_in_input_fields(self.material)
if not self.card_path:
if self.has_transient_mat is False:
self.add_transient_material()
else:
self.set_transient_material()
else:
# we found our exact material in self.materials dict :-)
FreeCAD.Console.PrintMessage(
"Material card was found in material directories. "
"We will use this material.\n"
)
index = self.parameterWidget.cb_materials.findData(self.card_path)
# print(index)
# set the current material in the cb widget
self.choose_material(index)
else:
self.set_transient_material()
FreeCAD.Console.PrintMessage(
'No changes where made by the material editor.\n'
)
# self.print_material_params()
# material editor returns the mat_dict only not a card_path, if a standard FreeCAD mat_card was used
# material editor returns the mat_dict only not a card_path
# if a standard FreeCAD mat_card was used
def toggleInputFieldsReadOnly(self):
if self.parameterWidget.chbu_allow_edit.isChecked():
@@ -280,10 +397,11 @@ class _TaskPanelFemMaterial:
self.parameterWidget.input_fd_kinematic_viscosity.setReadOnly(True)
self.parameterWidget.input_fd_vol_expansion_coefficient.setReadOnly(True)
# material parameter input fields *******************************
# material parameter input fields ************************************************************
def print_material_params(self, material=None):
# in rare cases we gone pass a empty dict
# in such a case a empty dict should be printed and not self.material thus we check for None
# in such a case a empty dict should be printed and not self.material
# thus we check for None
if material is None:
material = self.material
if not material:
@@ -297,72 +415,131 @@ class _TaskPanelFemMaterial:
def check_material_keys(self):
# FreeCAD units definition is at file end of src/Base/Unit.cpp
if not self.material:
print('For some reason all material data is empty!')
FreeCAD.Console.PrintMessage('For some reason all material data is empty!\n')
self.material['Name'] = 'Empty'
if 'Density' in self.material:
if 'Density' not in str(Units.Unit(self.material['Density'])):
print('Density in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
FreeCAD.Console.PrintMessage(
'Density in material data seems to have no unit '
'or a wrong unit (reset the value): {}\n'
.format(self.material['Name'])
)
self.material['Density'] = '0 kg/m^3'
else:
print('Density not found in material data of: ' + self.material['Name'])
FreeCAD.Console.PrintMessage(
'Density not found in material data of: {}\n'
.format(self.material['Name'])
)
self.material['Density'] = '0 kg/m^3'
if self.obj.Category == 'Solid':
# mechanical properties
if 'YoungsModulus' in self.material:
if 'Pressure' not in str(Units.Unit(self.material['YoungsModulus'])): # unit type of YoungsModulus is Pressure
print('YoungsModulus in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
# unit type of YoungsModulus is Pressure
if 'Pressure' not in str(Units.Unit(self.material['YoungsModulus'])):
FreeCAD.Console.PrintMessage(
'YoungsModulus in material data seems to have no unit '
'or a wrong unit (reset the value): {}\n'
.format(self.material['Name'])
)
self.material['YoungsModulus'] = '0 MPa'
else:
print('YoungsModulus not found in material data of: ' + self.material['Name'])
FreeCAD.Console.PrintMessage(
'YoungsModulus not found in material data of: {}\n'
.format(self.material['Name'])
)
self.material['YoungsModulus'] = '0 MPa'
if 'PoissonRatio' in self.material:
# PoissonRatio does not have a unit, but it is checked it there is no value at all
try:
float(self.material['PoissonRatio'])
except:
print('PoissonRatio has wrong or no data (reset the value): ' + self.material['PoissonRatio'])
FreeCAD.Console.PrintMessage(
'PoissonRatio has wrong or no data (reset the value): {}\n'
.format(self.material['PoissonRatio'])
)
self.material['PoissonRatio'] = '0'
else:
print('PoissonRatio not found in material data of: ' + self.material['Name'])
FreeCAD.Console.PrintMessage(
'PoissonRatio not found in material data of: {}\n'
.format(self.material['Name'])
)
self.material['PoissonRatio'] = '0'
if self.obj.Category == 'Fluid':
# Fluidic properties
if 'KinematicViscosity' in self.material:
if 'KinematicViscosity' not in str(Units.Unit(self.material['KinematicViscosity'])):
print('KinematicViscosity in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
ki_vis = self.material['KinematicViscosity']
if 'KinematicViscosity' not in str(Units.Unit(ki_vis)):
FreeCAD.Console.PrintMessage(
'KinematicViscosity in material data seems to have no unit '
'or a wrong unit (reset the value): {}\n'
.format(self.material['Name'])
)
self.material['KinematicViscosity'] = '0 m^2/s'
else:
print('KinematicViscosity not found in material data of: ' + self.material['Name'])
FreeCAD.Console.PrintMessage(
'KinematicViscosity not found in material data of: {}\n'
.format(self.material['Name'])
)
self.material['KinematicViscosity'] = '0 m^2/s'
if 'VolumetricThermalExpansionCoefficient' in self.material:
# unit type of VolumetricThermalExpansionCoefficient is ThermalExpansionCoefficient
if 'VolumetricThermalExpansionCoefficient' not in str(Units.Unit(self.material['VolumetricThermalExpansionCoefficient'])):
print('VolumetricThermalExpansionCoefficient in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
# unit type VolumetricThermalExpansionCoefficient is ThermalExpansionCoefficient
vol_ther_ex_co = self.material['VolumetricThermalExpansionCoefficient']
if 'VolumetricThermalExpansionCoefficient' not in str(Units.Unit(vol_ther_ex_co)):
FreeCAD.Console.PrintMessage(
'VolumetricThermalExpansionCoefficient in material data '
'seems to have no unit or a wrong unit (reset the value): {}\n'
.format(self.material['Name'])
)
self.material['VolumetricThermalExpansionCoefficient'] = '0 m/m/K'
else:
print('VolumetricThermalExpansionCoefficient not found in material data of: ' + self.material['Name'])
FreeCAD.Console.PrintMessage(
'VolumetricThermalExpansionCoefficient not found in material data of: {}\n'
.format(self.material['Name'])
)
self.material['VolumetricThermalExpansionCoefficient'] = '0 m/m/K'
# Thermal properties
if 'ThermalConductivity' in self.material:
if 'ThermalConductivity' not in str(Units.Unit(self.material['ThermalConductivity'])):
print('ThermalConductivity in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
FreeCAD.Console.PrintMessage(
'ThermalConductivity in material data seems to have no unit '
'or a wrong unit (reset the value): {}\n'
.format(self.material['Name'])
)
self.material['ThermalConductivity'] = '0 W/m/K'
else:
print('ThermalConductivity not found in material data of: ' + self.material['Name'])
FreeCAD.Console.PrintMessage(
'ThermalConductivity not found in material data of: {}\n'
.format(self.material['Name'])
)
self.material['ThermalConductivity'] = '0 W/m/K'
if 'ThermalExpansionCoefficient' in self.material:
if 'ThermalExpansionCoefficient' not in str(Units.Unit(self.material['ThermalExpansionCoefficient'])):
print('ThermalExpansionCoefficient in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
the_ex_co = self.material['ThermalExpansionCoefficient']
if 'ThermalExpansionCoefficient' not in str(Units.Unit(the_ex_co)):
FreeCAD.Console.PrintMessage(
'ThermalExpansionCoefficient in material data seems to have no unit '
'or a wrong unit (reset the value): {}\n'
.format(self.material['Name'])
)
self.material['ThermalExpansionCoefficient'] = '0 um/m/K'
else:
print('ThermalExpansionCoefficient not found in material data of: ' + self.material['Name'])
FreeCAD.Console.PrintMessage(
'ThermalExpansionCoefficient not found in material data of: {}\n'
.format(self.material['Name'])
)
self.material['ThermalExpansionCoefficient'] = '0 um/m/K'
if 'SpecificHeat' in self.material:
if 'SpecificHeat' not in str(Units.Unit(self.material['SpecificHeat'])):
print('SpecificHeat in material data seems to have no unit or a wrong unit (reset the value): ' + self.material['Name'])
FreeCAD.Console.PrintMessage(
'SpecificHeat in material data seems to have no unit '
'or a wrong unit (reset the value): {}\n'
.format(self.material['Name'])
)
self.material['SpecificHeat'] = '0 J/kg/K'
else:
print('SpecificHeat not found in material data of: ' + self.material['Name'])
FreeCAD.Console.PrintMessage(
'SpecificHeat not found in material data of: {}\n'
.format(self.material['Name'])
)
self.material['SpecificHeat'] = '0 J/kg/K'
# mechanical input fields
@@ -392,7 +569,8 @@ class _TaskPanelFemMaterial:
# density has changed
material = self.material
value_in_kg_per_m3 = value * 1e9
material['Density'] = unicode(value_in_kg_per_m3) + " kg/m^3" # SvdW:Keep density in SI units for easier readability
# SvdW:Keep density in SI units for easier readability
material['Density'] = unicode(value_in_kg_per_m3) + " kg/m^3"
self.material = material
if self.has_transient_mat is False:
self.add_transient_material()
@@ -442,7 +620,9 @@ class _TaskPanelFemMaterial:
def tec_changed(self):
value = self.parameterWidget.input_fd_expansion_coefficient.property("rawValue")
old_tec = Units.Quantity(self.material['ThermalExpansionCoefficient']).getValueAs("um/m/K")
old_tec = Units.Quantity(
self.material['ThermalExpansionCoefficient']
).getValueAs("um/m/K")
variation = 0.001
if value:
if not (1 - variation < float(old_tec) / value < 1 + variation):
@@ -475,14 +655,16 @@ class _TaskPanelFemMaterial:
# fluidic input fields
def vtec_changed(self):
value = self.parameterWidget.input_fd_vol_expansion_coefficient.property("rawValue")
old_vtec = Units.Quantity(self.material['VolumetricThermalExpansionCoefficient']).getValueAs("m/m/K")
old_vtec = Units.Quantity(
self.material['VolumetricThermalExpansionCoefficient']
).getValueAs("m/m/K")
variation = 0.001
if value:
if not (1 - variation < float(old_vtec) / value < 1 + variation):
# VolumetricThermalExpansionCoefficient has changed
material = self.material
value_in_one_per_K = value
material['VolumetricThermalExpansionCoefficient'] = unicode(value_in_one_per_K) + " m/m/K"
value_in_one_per_K = unicode(value) + " m/m/K"
material['VolumetricThermalExpansionCoefficient'] = value_in_one_per_K
self.material = material
if self.has_transient_mat is False:
self.add_transient_material()
@@ -521,7 +703,8 @@ class _TaskPanelFemMaterial:
nu_with_new_unit = nu.getValueAs(nu_new_unit)
q = FreeCAD.Units.Quantity("{} {}".format(nu_with_new_unit, nu_new_unit))
self.parameterWidget.input_fd_kinematic_viscosity.setText(q.UserString)
# For isotropic materials the volumetric thermal expansion coefficient is three times the linear coefficient:
# For isotropic materials the volumetric thermal expansion coefficient
# is three times the linear coefficient:
if 'VolumetricThermalExpansionCoefficient' in matmap: # linear, only for solid
vtec_new_unit = "m/m/K"
vtec = FreeCAD.Units.Quantity(matmap['VolumetricThermalExpansionCoefficient'])
@@ -532,7 +715,9 @@ class _TaskPanelFemMaterial:
density_new_unit = "kg/m^3"
density = FreeCAD.Units.Quantity(matmap['Density'])
density_with_new_unit = density.getValueAs(density_new_unit)
# self.parameterWidget.input_fd_density.setText("{} {}".format(density_with_new_unit, density_new_unit))
# self.parameterWidget.input_fd_density.setText(
# "{} {}".format(density_with_new_unit, density_new_unit)
# )
q = FreeCAD.Units.Quantity("{} {}".format(density_with_new_unit, density_new_unit))
self.parameterWidget.input_fd_density.setText(q.UserString)
# thermal properties
@@ -555,7 +740,20 @@ class _TaskPanelFemMaterial:
q = FreeCAD.Units.Quantity("{} {}".format(sh_with_new_unit, sh_new_unit))
self.parameterWidget.input_fd_specific_heat.setText(q.UserString)
# material import and export ************************************
# fill the combo box with cards **************************************************************
def add_cards_to_combo_box(self):
# fill combobox, in combo box the card name is used not the material name
from os.path import basename
self.parameterWidget.cb_materials.clear()
card_name_list = []
for a_path in self.materials:
card_name = basename(a_path[:-(len(".FCMat"))])
card_name_list.append([card_name, a_path, self.icons[a_path]])
card_name_list.sort()
for mat in card_name_list:
self.parameterWidget.cb_materials.addItem(QtGui.QIcon(mat[2]), mat[0], mat[1])
# material card handling *********************************************************************
def print_materialsdict(self):
print('\n\n')
for mat_card in self.materials:
@@ -564,10 +762,9 @@ class _TaskPanelFemMaterial:
print('\n\n')
def import_materials(self):
self.pathList = []
self.parameterWidget.cb_materials.clear()
self.fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/Resources")
self.fem_prefs = FreeCAD.ParamGet(
"User parameter:BaseApp/Preferences/Mod/Material/Resources"
)
if self.obj.Category == 'Fluid':
self.import_fluid_materials()
else:
@@ -607,18 +804,15 @@ class _TaskPanelFemMaterial:
self.add_cards_from_a_dir(custom_mat_dir, ":/icons/user.svg")
def add_cards_from_a_dir(self, mat_dir, icon):
# fill self.materials and self.icons
import glob
import os
import Material
mat_file_extension = ".FCMat"
ext_len = len(mat_file_extension)
dir_path_list = glob.glob(mat_dir + '/*' + mat_file_extension)
self.pathList = self.pathList + dir_path_list
card_name_list = []
from importFCMat import read
dir_path_list = glob.glob(mat_dir + '/*' + ".FCMat")
for a_path in dir_path_list:
card_name = os.path.basename(a_path[:-ext_len])
self.materials[a_path] = Material.importFCMat(a_path)
card_name_list.append([card_name, a_path])
card_name_list.sort()
for mat in card_name_list:
self.parameterWidget.cb_materials.addItem(QtGui.QIcon(icon), mat[0], mat[1])
mat_dict = read(a_path)
# check if the dict exists in materials
# TODO if the unit is different two cards would be different too
if mat_dict not in self.materials.values():
self.materials[a_path] = mat_dict
self.icons[a_path] = icon

View File

@@ -20,6 +20,9 @@
# * *
# ***************************************************************************
import FreeCAD
# here the usage description if you use this tool from the command line ("__main__")
CommandlineUsage = """Material - Tool to work with FreeCAD Material definition cards
@@ -49,8 +52,9 @@ Version:
"""
# see comments in module importFCMat, there is an independent parser implementation for reading and writing FCMat files
# inside FreeCAD a mixture of these parsers and the ones in importFCMat.py is used
# see comments in module importFCMat, there is an independent parser implementation
# for reading and writing FCMat files
# inside FreeCAD mostly the one from importFCMat.py is used
def importFCMat(fileName):
@@ -60,6 +64,10 @@ def importFCMat(fileName):
except ImportError:
import configparser
FreeCAD.Console.PrintError(
'This mat card reader is probably depretiated and not widely used in FreeCAD. '
'See comment in Material.py module.\n'
)
Config = configparser.RawConfigParser()
Config.optionxform = str
Config.read(fileName)
@@ -81,6 +89,10 @@ def exportFCMat(fileName, matDict):
import string
Config = configparser.RawConfigParser()
FreeCAD.Console.PrintError(
'This mat card writer is probably depretiated and not widely used in FreeCAD. '
'See comment in Material.py module.\n'
)
# create groups
for x in matDict.keys():
grp, key = string.split(x, sep='_')
@@ -104,7 +116,8 @@ def getMaterialAttributeStructure(withSpaces=None):
''''''
# material properties
# see the following resources in the FreeCAD wiki for more information about the material specific properties:
# see the following resources in the FreeCAD wiki for more information
# about the material specific properties:
# https://www.freecadweb.org/wiki/Material_data_model
# https://www.freecadweb.org/wiki/Material
@@ -115,7 +128,8 @@ def getMaterialAttributeStructure(withSpaces=None):
tree = ElementTree.parse(infile)
if withSpaces:
# on attributes, add a space before a capital letter, will be used for better display in the ui
# on attributes, add a space before a capital letter
# will be used for better display in the ui
import re
root = tree.getroot()
for group in root.getchildren():
@@ -131,7 +145,7 @@ def read_cards_from_path(cards_path):
from os.path import isfile, join, basename, splitext
from importFCMat import read
only_files = [f for f in listdir(cards_path) if isfile(join(cards_path, f))]
mat_files = [f for f in only_files if basename(splitext(f)[1]) == '.FCMat' or basename(splitext(f)[1]) == '.fcmat']
mat_files = [f for f in only_files if basename(splitext(f)[1]).upper() == '.FCMAT']
# print(mat_files)
mat_cards = []
for f in sorted(mat_files):

View File

@@ -20,23 +20,21 @@
# * *
# ***************************************************************************
from __future__ import print_function
import FreeCAD
import FreeCADGui
from Material import getMaterialAttributeStructure
import os
from PySide import QtCore, QtGui
# from PySide import QtUiTools, QtSvg
import sys
if sys.version_info.major >= 3:
unicode = str
__title__ = "FreeCAD material editor"
__author__ = "Yorik van Havre"
__url__ = "http://www.freecadweb.org"
import os
import sys
from PySide import QtCore, QtGui
# from PySide import QtUiTools, QtSvg
import FreeCAD
import FreeCADGui
if sys.version_info.major >= 3:
unicode = str
class MaterialEditor:
@@ -78,7 +76,8 @@ class MaterialEditor:
standardButtons.button(QtGui.QDialogButtonBox.Ok).setAutoDefault(False)
standardButtons.button(QtGui.QDialogButtonBox.Cancel).setAutoDefault(False)
self.updateCards()
# TODO allow to enter a custom property by pressing Enter in the lineedit (currently closes the dialog)
# TODO allow to enter a custom property by pressing Enter in the lineedit
# currently closes the dialog
standardButtons.rejected.connect(self.reject)
standardButtons.accepted.connect(self.accept)
@@ -119,6 +118,7 @@ class MaterialEditor:
treeView.setColumnWidth(1, 250)
treeView.setColumnHidden(2, True)
from Material import getMaterialAttributeStructure
tree = getMaterialAttributeStructure(True)
MatPropDict = tree.getroot()
@@ -149,9 +149,12 @@ class MaterialEditor:
def updateContents(self, data):
'''updates the contents of the editor with the given data, can be:
- the name of a card, if material is changed in editors combo box
- a dictionary, if the editor was called with data.'''
- a dictionary, if the editor was called with data
- a string, the name of a card, if material is changed in editors combo box
the material property keys where added to the editor already
not known material property keys will be added to the user defined group'''
# print type(data)
if isinstance(data, dict):
# a standard material property dict is provided
model = self.widget.treeView.model()
@@ -182,18 +185,21 @@ class MaterialEditor:
self.customprops.append(k)
elif isinstance(data, unicode):
# a card name is provided, search card, read material data and call this def once more with std material property dict
# a card name is provided, search card, read material data and call
# this def once more with std material property dict
k = str(data)
if k:
if k in self.cards:
import importFCMat
d = importFCMat.read(self.cards[k])
from importFCMat import read
d = read(self.cards[k])
if d:
self.updateContents(d)
def getMaterialResources(self):
self.fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/Resources")
self.fem_prefs = FreeCAD.ParamGet(
"User parameter:BaseApp/Preferences/Mod/Material/Resources"
)
use_built_in_materials = self.fem_prefs.GetBool("UseBuiltInMaterials", True)
use_mat_from_config_dir = self.fem_prefs.GetBool("UseMaterialsFromConfigDir", True)
use_mat_from_custom_dir = self.fem_prefs.GetBool("UseMaterialsFromCustomDir", True)
@@ -203,7 +209,10 @@ class MaterialEditor:
# FreeCAD returns paths with / at the end, thus not os.sep is needed on first +
self.resources = []
if use_built_in_materials:
self.resources.append(FreeCAD.getResourceDir() + "Mod" + os.sep + "Material" + os.sep + "StandardMaterial")
res_dir = FreeCAD.getResourceDir()
self.resources.append(
res_dir + "Mod" + os.sep + "Material" + os.sep + "StandardMaterial"
)
if use_mat_from_config_dir:
self.resources.append(FreeCAD.ConfigGet("UserAppData") + "Material")
if use_mat_from_custom_dir:
@@ -220,12 +229,14 @@ class MaterialEditor:
def outputCards(self):
print('material cards:')
for card in self.cards:
for card in sorted(self.cards.keys()):
print(' ' + card + ': ' + self.cards[card])
print('\n')
def updateCards(self):
"updates the contents of the materials combo with existing material cards"
'''updates the contents of the materials combo with existing material cards'''
self.getMaterialResources()
self.cards = {}
for p in self.resources:
@@ -238,11 +249,12 @@ class MaterialEditor:
if self.cards:
self.widget.ComboMaterial.clear()
self.widget.ComboMaterial.addItem("") # add a blank item first
for k, i in self.cards.items():
self.widget.ComboMaterial.addItem(k)
for card in sorted(self.cards.keys()):
self.widget.ComboMaterial.addItem(card) # all keys in self.cards are unicode
def openProductURL(self):
"opens the contents of the ProductURL field in an external browser."
'''opens the contents of the ProductURL field in an external browser.'''
model = self.widget.treeView.model()
item = model.findItems(translate("Material", "Product URL"),
@@ -383,7 +395,8 @@ class MaterialEditor:
kk = group.child(row, 0).text()
ii = group.child(row, 1).text()
# TODO the following should be translated back to english,since text(0) could be translated
# TODO the following should be translated back to english
# since text(0) could be translated
matkey = self.collapseKey(str(kk))
matvalue = unicode(ii)
if matvalue or (matkey == 'Name'):
@@ -395,9 +408,10 @@ class MaterialEditor:
def outputDict(self, d):
print('MaterialEditor dictionary')
for param in d:
print(' ' + param + ' : ' + d[param])
print(' {} : {}'.format(param, d[param]))
'''def setTexture(self, pattern):
'''
def setTexture(self, pattern):
"displays a texture preview if needed"
self.widget.PreviewVector.hide()
if pattern:
@@ -409,16 +423,23 @@ class MaterialEditor:
pattern = DrawingPatterns.buildFileSwatch(pattern, size=96, png=True)
if pattern:
self.widget.PreviewVector.setPixmap(QtGui.QPixmap(pattern))
self.widget.PreviewVector.show()'''
self.widget.PreviewVector.show()
'''
def openfile(self):
"Opens a FCMat file"
filetuple = QtGui.QFileDialog.getOpenFileName(QtGui.QApplication.activeWindow(), 'Open FreeCAD Material file', self.directory, '*.FCMat')
filename = filetuple[0] # a tuple of two empty strings returns True, so use the filename directly
filetuple = QtGui.QFileDialog.getOpenFileName(
QtGui.QApplication.activeWindow(),
'Open FreeCAD Material file',
self.directory,
'*.FCMat'
)
# a tuple of two empty strings returns True, so use the filename directly
filename = filetuple[0]
if filename:
import importFCMat
from importFCMat import read
self.directory = os.path.dirname(filename)
d = importFCMat.read(filename)
d = read(filename)
if d:
self.updateContents(d)
@@ -436,18 +457,21 @@ class MaterialEditor:
name = name.encode("utf8")
if not name:
name = "Material"
filetuple =\
QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(),
'Save FreeCAD Material file',
self.directory + '/' + name + '.FCMat', '*.FCMat')
filename = filetuple[0] # a tuple of two empty strings returns True, so use the filename directly
filetuple = QtGui.QFileDialog.getSaveFileName(
QtGui.QApplication.activeWindow(),
'Save FreeCAD Material file',
self.directory + '/' + name + '.FCMat',
'*.FCMat'
)
# a tuple of two empty strings returns True, so use the filename directly
filename = filetuple[0]
if filename:
self.directory = os.path.dirname(filename)
d = self.getDict()
# self.outputDict(d)
if d:
import importFCMat
importFCMat.write(filename, d)
from importFCMat import write
write(filename, d)
self.updateCards()
def show(self):
@@ -567,7 +591,7 @@ def matProperWidget(parent=None, matproperty=None, Type="String", Value=None,
quantity = FreeCAD.Units.Quantity(1, unit)
widget.setProperty('unit', quantity.getUserPreferred()[2])
else:
FreeCAD.Console.PrintError('Not known unit for property: ' + matproperty + '\n')
FreeCAD.Console.PrintError('Not known unit for property: {}\n'.format(matproperty))
elif Type == "Integer":
@@ -644,9 +668,11 @@ def editMaterial(material):
"""editMaterial(material): opens the editor to edit the contents
of the given material dictionary. Returns the modified material dictionary."""
# if the material editor is opened with this def the combo box with the card name is empty
# this makes sense, because the editor was not opened with a card but with material dictionary instead
# this makes sense ...
# because the editor was not opened with a card but with material dictionary instead
# TODO: add some text in combo box, may be "custom material data" or "user material data"
# TODO: all card could be checked if one fits exact ALL provided data and than this card name could be displayed
# TODO: all card could be checked if one fits exact ALL provided data
# than this card name could be displayed
editor = MaterialEditor(material=material)
result = editor.exec_()
if result:
@@ -660,12 +686,19 @@ def editMaterial(material):
import MaterialEditor
MaterialEditor.openEditor()
doc = FreeCAD.open(FreeCAD.ConfigGet("AppHomePath") + 'data/examples/FemCalculixCantilever3D.FCStd')
doc = FreeCAD.open(
FreeCAD.ConfigGet("AppHomePath") + 'data/examples/FemCalculixCantilever3D.FCStd'
)
import MaterialEditor
MaterialEditor.openEditor('SolidMaterial', 'Material')
import MaterialEditor
MaterialEditor.editMaterial({'Density': '1234.0 kg/m^3', 'Name': 'My-Material-Data', 'PoissonRatio': '0.66', 'YoungsModulus': '123456 MPa'})
MaterialEditor.editMaterial({
'Density': '1234.0 kg/m^3',
'Name': 'My-Material-Data',
'PoissonRatio': '0.66',
'YoungsModulus': '123456 MPa'
})
import MaterialEditor
MaterialEditor.editMaterial('ABS')

View File

@@ -78,13 +78,16 @@ def decode(name):
return decodedName
# the reader and writer do not use some Library to read and write the ini file format, they are implemented here
# the reader and writer do not use some Library to read and write the ini file format
# they are implemented here
# thus non standard ini files will be read and written too
# in standard ini file format a = in the value without any encapsulation or string quotes is not allowed (AFAIK)
# in standard ini file format:
# a = in the value without any encapsulation or string quotes is not allowed (AFAIK)
# https://en.wikipedia.org/wiki/INI_file
# http://www.docuxplorer.com/WebHelp/INI_File_Format.htm
# mainly this parser here is used in FreeCAD
# in the module Material.py is another implementation of reading and writing FCMat files which uses the module ConfigParser
# in the module Material.py is another implementation of reading and writing FCMat files
# this implementation uses the ConfigParser module
# in ViewProviderFemMaterial in add_cards_from_a_dir() the parser from Material.py is used
# since this mixture seams to be there for ages it should not be changed for 0.18
# TODO: get rid of this mixture in FreeCAD 0.19
@@ -117,7 +120,10 @@ def read(filename):
v = v.decode('utf-8')
card_name_content = v
if card_name_content != d["CardName"]:
FreeCAD.Console.PrintError("File CardName (" + card_name_file + ") is not content CardName (" + card_name_content + ")\n")
FreeCAD.Console.PrintLog(
"File CardName ( {} ) is not content CardName ( {} )\n"
.format(card_name_file, card_name_content)
)
elif ln == 1:
v = line.split(";")[1].strip() # Line 2
if hasattr(v, "decode"):
@@ -128,7 +134,9 @@ def read(filename):
# # might be a comment too ?
# [ is a Section
if line[0] not in ";#[":
k = line.split("=", 1) # only split once on first occurrence, a link could contain a = and thus would be splitted
# split once on first occurrence
# a link could contain a = and thus would be split
k = line.split("=", 1)
if len(k) == 2:
v = k[1].strip()
if hasattr(v, "decode"):
@@ -166,12 +174,18 @@ def write(filename, dictionary, write_group_section=True):
user[k] = i
# delete empty properties
for group in contents:
for k in list(group.keys()): # iterating over a dict and changing it is not allowed, thus we iterate over a list of the keys
# iterating over a dict and changing it is not allowed
# thus it is iterated over a list of the keys
for k in list(group.keys()):
if group[k] == '':
del group[k]
# card writer
rev = FreeCAD.ConfigGet("BuildVersionMajor") + "." + FreeCAD.ConfigGet("BuildVersionMinor") + "." + FreeCAD.ConfigGet("BuildRevision")
rev = "{}.{}.{}".format(
FreeCAD.ConfigGet("BuildVersionMajor"),
FreeCAD.ConfigGet("BuildVersionMinor"),
FreeCAD.ConfigGet("BuildRevision")
)
if isinstance(filename, unicode):
if sys.version_info.major < 3:
filename = filename.encode(sys.getfilesystemencoding())
@@ -182,7 +196,8 @@ def write(filename, dictionary, write_group_section=True):
# write header
# first five lines are the same in any card file, see comment above read def
if header["CardName"] != card_name_file:
FreeCAD.Console.PrintMessage("File CardName is used: " + card_name_file + " \n") # CardName is the MatCard file name
# CardName is the MatCard file name
FreeCAD.Console.PrintMessage("File CardName is used: {}\n".format(card_name_file))
if sys.version_info.major >= 3:
f.write("; " + card_name_file + "\n")
f.write("; " + header["AuthorAndLicense"] + "\n")

View File

@@ -17,77 +17,70 @@
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="ButtonURL">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>Opens the Product URL of this material in an external browser</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Material card:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="ComboMaterial">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Existing material cards</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="ButtonOpen">
<property name="toolTip">
<string>Opens an existing material card</string>
</property>
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ButtonSave">
<property name="toolTip">
<string>Saves this material as a card</string>
</property>
<property name="text">
<string>Save as...</string>
</property>
</widget>
</item>
</layout>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Material card</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QPushButton" name="ButtonURL">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>Opens the Product URL of this material in an external browser</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="ComboMaterial">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Existing material cards</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="ButtonOpen">
<property name="toolTip">
<string>Opens an existing material card</string>
</property>
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ButtonSave">
<property name="toolTip">
<string>Saves this material as a card</string>
</property>
<property name="text">
<string>Save as...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="PreviewGroup">
@@ -138,19 +131,28 @@
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Material parameter</string>
</property>
<item>
<widget class="QTreeView" name="treeView"/>
</item>
</layout>
<layout class="QVBoxLayout" name="verticalLayout_5">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="sizeConstraint">
<enum>QLayout::SetMaximumSize</enum>
</property>
<item>
<widget class="QTreeView" name="treeView"/>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Add / Remove</string>
<string>Add / remove parameter</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<item>