diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterial.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterial.py index 325214b26d..dbbfc056c2 100644 --- a/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterial.py +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemMaterial.py @@ -1,6 +1,6 @@ # *************************************************************************** -# * * -# * Copyright (c) 2013 Juergen Riegel * +# * Copyright (c) 2013 Juergen Riegel * +# * Copyright (c) 2016 Bernd Hahnebach * # * * # * 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 diff --git a/src/Mod/Material/Material.py b/src/Mod/Material/Material.py index 837bb8c718..d62548ca39 100644 --- a/src/Mod/Material/Material.py +++ b/src/Mod/Material/Material.py @@ -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): diff --git a/src/Mod/Material/MaterialEditor.py b/src/Mod/Material/MaterialEditor.py index f10ea58096..b9cde2a69a 100644 --- a/src/Mod/Material/MaterialEditor.py +++ b/src/Mod/Material/MaterialEditor.py @@ -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') diff --git a/src/Mod/Material/importFCMat.py b/src/Mod/Material/importFCMat.py index 4e14ca5e81..f6e6ddfe52 100644 --- a/src/Mod/Material/importFCMat.py +++ b/src/Mod/Material/importFCMat.py @@ -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") diff --git a/src/Mod/Material/materials-editor.ui b/src/Mod/Material/materials-editor.ui index 7b29f1e6cf..ef1990540c 100644 --- a/src/Mod/Material/materials-editor.ui +++ b/src/Mod/Material/materials-editor.ui @@ -17,77 +17,70 @@ - - - - - - 22 - 22 - - - - Opens the Product URL of this material in an external browser - - - - - - - - - - Material card: - - - - - - - - 120 - 0 - - - - Existing material cards - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Opens an existing material card - - - Open... - - - - - - - Saves this material as a card - - - Save as... - - - - + + + Material card + + + + + + + + + 22 + 22 + + + + Opens the Product URL of this material in an external browser + + + + + + + + + + + 120 + 0 + + + + Existing material cards + + + + + + + + + + + Opens an existing material card + + + Open... + + + + + + + Saves this material as a card + + + Save as... + + + + + + + @@ -138,19 +131,28 @@ - - - QLayout::SetMaximumSize + + + Material parameter - - - - + + + + + QLayout::SetMaximumSize + + + + + + + + - Add / Remove + Add / remove parameter