diff --git a/src/Mod/Fem/Gui/Resources/ui/Material.ui b/src/Mod/Fem/Gui/Resources/ui/Material.ui index 7efe5269b8..69514b5ef5 100755 --- a/src/Mod/Fem/Gui/Resources/ui/Material.ui +++ b/src/Mod/Fem/Gui/Resources/ui/Material.ui @@ -15,122 +15,7 @@ - - - - 16777215 - 1677215 - - - - Material - - - - - - - - Category - - - - - - - - - - - - - - Material card - - - - - - - - choose... - - - - - - - - Material name - - - - - - - TextLabel - - - - - - - - - Material Description - - - true - - - - - - - - - - - 16777215 - 1677215 - - - - Editing material - - - - - - - - Use FreeCAD material editor - - - - - - - - 0 - 0 - - - - Qt::RightToLeft - - - Use this task panel - - - false - - - - - - - + @@ -590,18 +475,21 @@ - - - - Qt::Vertical + + + + + 0 + 0 + - - - 20 - 40 - + + Use this task panel - + + false + + @@ -611,6 +499,11 @@ QLineEdit
Gui/InputField.h
+ + MatGui::MaterialTreeWidget + QWidget +
Mod/Material/Gui/MaterialTreeWidget.h
+
diff --git a/src/Mod/Fem/femobjects/material_common.py b/src/Mod/Fem/femobjects/material_common.py index f230b843cc..10011c7978 100644 --- a/src/Mod/Fem/femobjects/material_common.py +++ b/src/Mod/Fem/femobjects/material_common.py @@ -70,6 +70,16 @@ class MaterialCommon(base_fempythonobject.BaseFemPythonObject): value=["Solid", "Fluid"], ) ) + prop.append( + _PropHelper( + type="App::PropertyString", + name="UUID", + group="Material", + doc="Material UUID", + hidden=True, + value="", + ) + ) return prop diff --git a/src/Mod/Fem/femtaskpanels/task_material_common.py b/src/Mod/Fem/femtaskpanels/task_material_common.py index d2c1afdc94..36a1050520 100644 --- a/src/Mod/Fem/femtaskpanels/task_material_common.py +++ b/src/Mod/Fem/femtaskpanels/task_material_common.py @@ -37,6 +37,8 @@ from PySide import QtGui import FreeCAD import FreeCADGui from FreeCAD import Units +import Materials +import MatGui from femguiutils import selection_widgets from . import base_femtaskpanel @@ -50,35 +52,25 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): def __init__(self, obj): super().__init__(obj) - self.material = self.obj.Material # FreeCAD material dictionary of current material - self.card_path = "" - self.materials = {} # { card_path : FreeCAD material dict, ... } - self.cards = {} # { card_path : card_names, ... } - 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 - self.has_transient_mat = False + # FreeCAD material dictionary of current material + self.material = self.obj.Material + self.uuid = self.obj.UUID + self.material_manager = Materials.MaterialManager() # parameter widget 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 - ) + self.material_tree = MatGui.MaterialTreeWidget(self.parameterWidget.wgt_material_tree) + self.material_tree.expanded = False + self.material_tree.IncludeEmptyFolders = False + self.material_tree.IncludeEmptyLibraries = False + 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, @@ -123,6 +115,11 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): QtCore.SIGNAL("editingFinished()"), self.vtec_changed, ) + QtCore.QObject.connect( + self.parameterWidget.wgt_material_tree, + QtCore.SIGNAL("onMaterial(QString)"), + self.set_from_editor, + ) # init all parameter input files with read only self.parameterWidget.chbu_allow_edit.setCheckState(QtCore.Qt.CheckState.Unchecked) @@ -131,8 +128,8 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): # hide some groupBox according to material category # note: input_fd_vol_expansion_coefficient is currently not used # it might be used in future for solids - self.parameterWidget.label_category.setText(self.obj.Category) if self.obj.Category == "Fluid": + self.filter_models(self.obj.Category) self.parameterWidget.groupBox_mechanical.setVisible(0) self.parameterWidget.label_vol_expansion_coefficient.setVisible(0) self.parameterWidget.input_fd_vol_expansion_coefficient.setVisible(0) @@ -141,43 +138,6 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): self.parameterWidget.label_vol_expansion_coefficient.setVisible(0) self.parameterWidget.input_fd_vol_expansion_coefficient.setVisible(0) - # get all available materials (fill self.materials, self.cards and self.icons) - from materialtools.cardutils import import_materials as getmats - - # Note: import_materials(category="Solid", ...), - # category default to Solid, but must be given for FluidMaterial to be imported - self.materials, self.cards, self.icons = getmats(self.obj.Category) - # 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) - FreeCAD.Console.PrintLog(f"card_path: {self.card_path}\n") - 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 can not 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 - ) - index = self.parameterWidget.cb_materials.findData(self.card_path) - # 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.PrintLog( - "Previously used material card was found in material directories. " - "We will use this material.\n" - ) - index = self.parameterWidget.cb_materials.findData(self.card_path) - # fill input fields and set the current material in the cb widget - self.choose_material(index) - # geometry selection widget self.selectionWidget = selection_widgets.GeometryElementsSelection( obj.References, ["Solid", "Face", "Edge"], False, True @@ -187,21 +147,21 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): self.form = [self.parameterWidget, self.selectionWidget] # check references, has to be after initialisation of selectionWidget + self.material_tree.UUID = self.get_material_uuid(self.material) + self.check_material_keys() + self.set_mat_params_in_input_fields(self.material) + self.selectionWidget.has_equal_references_shape_types() # leave task panel *************************************************************************** def accept(self): - # print(self.material) - if self.material == {}: # happens if material editor was canceled - FreeCAD.Console.PrintError("Empty material dictionary, nothing was changed.\n") - self.recompute_and_set_back_all() - return True if self.selectionWidget.has_equal_references_shape_types(): self.do_not_set_thermal_zeros() from materialtools.cardutils import check_mat_units as checkunits if checkunits(self.material) is True: self.obj.Material = self.material + self.obj.UUID = self.uuid self.obj.References = self.selectionWidget.references else: error_message = ( @@ -249,20 +209,39 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): return False # choose material **************************************************************************** - def get_material_card(self, material): - for a_mat in self.materials: + def filter_models(self, category): + material_filter = Materials.MaterialFilter() + models = set(Materials.ModelManager().Models.keys()) + uuids = Materials.UUIDs() + if category == "Fluid": + material_filter.RequiredModels = [uuids.Fluid] + self.material_tree.setFilter(material_filter) + + def get_material_uuid(self, material): + if self.uuid: + try: + self.material_manager.getMaterial(self.uuid) + return self.uuid + except: + return "" + + if not self.material: + return "" + + for a_mat in self.material_manager.Materials: # check if every item of the current material fits to a known material card # if all items were found we know it is the right card # we can hereby not simply perform # set(self.materials[a_mat].items()) ^ set(material.items()) # because entries are often identical, just appear in the set in a different order unmatched_item = False + a_mat_prop = self.material_manager.getMaterial(a_mat).Properties.items() for item in material.items(): - if item not in self.materials[a_mat].items(): + if item not in a_mat_prop: unmatched_item = True # often the difference is just a decimal e.g. "120" to "120.0" # therefore first check if the item name exists - for a_mat_item in self.materials[a_mat].items(): + for a_mat_item in a_mat_prop: if item[0] == a_mat_item[0]: # now check if we have a number value in a unit if item[1].split() and not self.isfloat(item[1].split()[0]): @@ -282,99 +261,6 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): return a_mat return "" - def choose_material(self, index): - if index < 0: - return - self.card_path = self.parameterWidget.cb_materials.itemData(index) # returns whole path - FreeCAD.Console.PrintMessage(f"Material card chosen:\n {self.card_path}\n") - self.material = self.materials[self.card_path] - self.check_material_keys() - self.set_mat_params_in_input_fields(self.material) - self.parameterWidget.cb_materials.setCurrentIndex(index) # set after input fields - gen_mat_desc = "" - gen_mat_name = "" - if "Description" in self.material: - gen_mat_desc = self.material["Description"] - if "Name" in self.material: - gen_mat_name = self.material["Name"] - self.parameterWidget.l_mat_description.setText(gen_mat_desc) - self.parameterWidget.l_mat_name.setText(gen_mat_name) - # print("choose_material: done") - - def set_transient_material(self): - self.card_path = "_transient_material" - self.materials[self.card_path] = self.material # = the current input fields data - index = self.parameterWidget.cb_materials.findData(self.card_path) - self.choose_material(index) - - 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.set_transient_material() - - # how to edit a material ********************************************************************* - def edit_material(self): - # opens the material editor to choose a material or edit material params - import MaterialEditor - - if self.card_path not in self.cards: - FreeCAD.Console.PrintLog( - "Card path not in cards, material dict will be used to open Material Editor.\n" - ) - new_material_params = MaterialEditor.editMaterial( - material=self.material, category=self.obj.Category - ) - else: - new_material_params = MaterialEditor.editMaterial( - card_path=self.card_path, category=self.obj.Category - ) - # material editor returns the mat_dict only, not a card_path - # if the material editor was canceled a empty dict will be returned - # do not change the self.material - # check if dict is not empty (do not use "is True") - if new_material_params: - # check material quantity units - from materialtools.cardutils import check_mat_units as checkunits - - if checkunits(new_material_params) is True: - self.material = new_material_params - self.card_path = self.get_material_card(self.material) - self.check_material_keys() - self.set_mat_params_in_input_fields(self.material) - if not self.card_path: - FreeCAD.Console.PrintMessage( - "Material card chosen by the material editor " - "was not found in material directories.\n" - "Either the card does not exist or some material " - "parameter where changed in material editor.\n" - ) - 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.PrintLog( - "Material card chosen by the material editor " - "was found in material directories. " - "The found material card will be used.\n" - ) - index = self.parameterWidget.cb_materials.findData(self.card_path) - # set the current material in the cb widget - self.choose_material(index) - else: - error_message = ( - "Due to some wrong material quantity units in data passed " - "by the material editor, the material data was not changed.\n" - ) - FreeCAD.Console.PrintError(error_message) - QtGui.QMessageBox.critical(None, "Material data not changed", error_message) - else: - FreeCAD.Console.PrintLog("No changes where made by the material editor.\n") - def toggleInputFieldsReadOnly(self): if self.parameterWidget.chbu_allow_edit.isChecked(): self.parameterWidget.input_fd_density.setReadOnly(False) @@ -575,11 +461,6 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): else: material[matProperty] = str(value) self.material = material - if self.has_transient_mat is False: - self.add_transient_material() - else: - self.set_transient_material() - # print(inputfield_text) # mechanical input fields def ym_changed(self): @@ -616,10 +497,6 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): material = self.material material["PoissonRatio"] = str(value) self.material = material - if self.has_transient_mat is False: - self.add_transient_material() - else: - self.set_transient_material() # thermal input fields def tc_changed(self): @@ -712,29 +589,9 @@ class _TaskPanel(base_femtaskpanel._BaseTaskPanel): q = Units.Quantity(f"{sh_with_new_unit} {sh_new_unit}") self.parameterWidget.input_fd_specific_heat.setText(q.UserString) - # 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 - self.parameterWidget.cb_materials.clear() - - mat_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/Cards") - sort_by_resources = mat_prefs.GetBool("SortByResources", False) - - card_name_list = [] # [ [card_name, card_path, icon_path], ... ] - - if sort_by_resources is True: - for a_path in sorted(self.materials): - card_name_list.append([self.cards[a_path], a_path, self.icons[a_path]]) - else: - card_names_tmp = {} - for path, name in self.cards.items(): - card_names_tmp[name] = path - for a_name in sorted(card_names_tmp): - a_path = card_names_tmp[a_name] - card_name_list.append([a_name, a_path, self.icons[a_path]]) - - for mat in card_name_list: - self.parameterWidget.cb_materials.addItem(QtGui.QIcon(mat[2]), mat[0], mat[1]) - # the whole card path is added to the combo box to make it unique - # see def choose_material: - # for assignment of self.card_path the path form the parameterWidget is used + def set_from_editor(self, value): + mat = self.material_manager.getMaterial(value) + self.material = mat.Properties + self.uuid = mat.UUID + self.check_material_keys() + self.set_mat_params_in_input_fields(self.material)