diff --git a/src/Mod/Fem/Gui/DlgSettingsFemMaterial.ui b/src/Mod/Fem/Gui/DlgSettingsFemMaterial.ui index 7bb12a8e88..59e659f67d 100644 --- a/src/Mod/Fem/Gui/DlgSettingsFemMaterial.ui +++ b/src/Mod/Fem/Gui/DlgSettingsFemMaterial.ui @@ -6,7 +6,7 @@ 0 0 - 505 + 519 451 @@ -20,7 +20,7 @@ - Material Resources + Card resources @@ -126,6 +126,51 @@ + + + + Card sorting and duplicates + + + + + + + + Delete card duplicates + + + true + + + DeleteDuplicates + + + Mod/Material/Cards + + + + + + + Sort by resources (opposite would be sort by cards) + + + true + + + SortByResources + + + Mod/Material/Cards + + + + + + + + diff --git a/src/Mod/Fem/Gui/DlgSettingsFemMaterialImp.cpp b/src/Mod/Fem/Gui/DlgSettingsFemMaterialImp.cpp index d2f73c15bb..e6373a55c9 100644 --- a/src/Mod/Fem/Gui/DlgSettingsFemMaterialImp.cpp +++ b/src/Mod/Fem/Gui/DlgSettingsFemMaterialImp.cpp @@ -48,6 +48,8 @@ void DlgSettingsFemMaterialImp::saveSettings() cb_use_mat_from_config_dir->onSave(); cb_use_mat_from_custom_dir->onSave(); fc_custom_mat_dir->onSave(); + cb_delete_duplicates->onSave(); + cb_sort_by_resources->onSave(); } void DlgSettingsFemMaterialImp::loadSettings() @@ -56,6 +58,8 @@ void DlgSettingsFemMaterialImp::loadSettings() cb_use_mat_from_config_dir->onRestore(); cb_use_mat_from_custom_dir->onRestore(); fc_custom_mat_dir->onRestore(); + cb_delete_duplicates->onRestore(); + cb_sort_by_resources->onRestore(); } /** diff --git a/src/Mod/Material/MaterialEditor.py b/src/Mod/Material/MaterialEditor.py index a1edfe5431..5502c566e7 100644 --- a/src/Mod/Material/MaterialEditor.py +++ b/src/Mod/Material/MaterialEditor.py @@ -32,6 +32,7 @@ from PySide import QtCore, QtGui import FreeCAD import FreeCADGui +# is this still needed after the move to card utils??? if sys.version_info.major >= 3: unicode = str @@ -50,6 +51,9 @@ class MaterialEditor: self.internalprops = [] self.groups = [] self.directory = FreeCAD.getResourceDir() + "Mod/Material" + self.materials = {} + self.cards = {} + self.icons = {} # load the UI file from the same directory as this script self.widget = FreeCADGui.PySideUic.loadUi( @@ -75,7 +79,7 @@ class MaterialEditor: buttonDeleteProperty.setEnabled(False) standardButtons.button(QtGui.QDialogButtonBox.Ok).setAutoDefault(False) standardButtons.button(QtGui.QDialogButtonBox.Cancel).setAutoDefault(False) - self.updateCards() + self.updateCardsInCombo() # TODO allow to enter a custom property by pressing Enter in the lineedit # currently closes the dialog @@ -84,7 +88,7 @@ class MaterialEditor: buttonOpen.clicked.connect(self.openfile) buttonSave.clicked.connect(self.savefile) buttonURL.clicked.connect(self.openProductURL) - comboMaterial.currentIndexChanged[str].connect(self.updateContents) + comboMaterial.currentIndexChanged[str].connect(self.updateMatParamsInTree) buttonAddProperty.clicked.connect(self.addCustomProperty) buttonDeleteProperty.clicked.connect(self.deleteCustomProperty) treeView.clicked.connect(self.checkDeletable) @@ -103,7 +107,7 @@ class MaterialEditor: self.implementModel() if d: - self.updateContents(d) + self.updateMatParamsInTree(d) def implementModel(self): @@ -146,7 +150,7 @@ class MaterialEditor: treeView.expandAll() - def updateContents(self, data): + def updateMatParamsInTree(self, data): '''updates the contents of the editor with the given data, can be: - a dictionary, if the editor was called with data @@ -191,66 +195,39 @@ class MaterialEditor: if k: if k in self.cards: + # be careful with reading from materials dict + # the card could be updated the dict not from importFCMat import read d = read(self.cards[k]) if d: - self.updateContents(d) + self.updateMatParamsInTree(d) - def getMaterialResources(self): - 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) - if use_mat_from_custom_dir: - custom_mat_dir = self.fem_prefs.GetString("CustomMaterialsDir", "") - # later found cards with same name will override cards - # FreeCAD returns paths with / at the end, thus not os.sep is needed on first + - self.resources = [] - if use_built_in_materials: - 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: - custom_mat_dir = self.fem_prefs.GetString("CustomMaterialsDir", "") - if os.path.exists(custom_mat_dir): - self.resources.append(custom_mat_dir) - self.outputResources() - - def outputResources(self): - print('locations to look for material cards:') - for path in self.resources: - print(' ' + path) - print('\n') - - def outputCards(self): - print('material cards:') - for card in sorted(self.cards.keys()): - print(' ' + card + ': ' + self.cards[card]) - print('\n') - - def updateCards(self): + def updateCardsInCombo(self): '''updates the contents of the materials combo with existing material cards''' - self.getMaterialResources() - self.cards = {} - for p in self.resources: - if os.path.exists(p): - for f in sorted(os.listdir(p)): - b, e = os.path.splitext(f) - if e.upper() == ".FCMAT": - self.cards[b] = p + os.sep + f - # self.outputCards() - if self.cards: - self.widget.ComboMaterial.clear() - self.widget.ComboMaterial.addItem("") # add a blank item first - for card in sorted(self.cards.keys()): - self.widget.ComboMaterial.addItem(card) # all keys in self.cards are unicode + mat_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/Cards") + sort_by_resources = mat_prefs.GetBool("SortByResources", False) + + # get all available materials (fill self.materials, self.cards and self.icons) + from materialtools.cardutils import import_materials as getmats + self.materials, self.cards, self.icons = getmats() + + card_name_list = [] # [ [card_name, card_path, icon_path], ... ] + + if sort_by_resources is True: + for a_path in sorted(self.materials.keys()): + 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.keys()): + 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.widget.ComboMaterial.addItem(QtGui.QIcon(mat[2]), mat[0]) def openProductURL(self): @@ -441,7 +418,7 @@ class MaterialEditor: self.directory = os.path.dirname(filename) d = read(filename) if d: - self.updateContents(d) + self.updateMatParamsInTree(d) def savefile(self): "Saves a FCMat file." @@ -472,7 +449,7 @@ class MaterialEditor: if d: from importFCMat import write write(filename, d) - self.updateCards() + self.updateCardsInCombo() def show(self): return self.widget.show() diff --git a/src/Mod/Material/materialtools/cardutils.py b/src/Mod/Material/materialtools/cardutils.py index c4be16f95f..e28136440b 100644 --- a/src/Mod/Material/materialtools/cardutils.py +++ b/src/Mod/Material/materialtools/cardutils.py @@ -23,3 +23,159 @@ __title__ = "material cards utilities" __author__ = "Bernd Hahnebach" __url__ = "http://www.freecadweb.org" +import os +import sys +from os.path import join + +import FreeCAD + + +if sys.version_info.major >= 3: + unicode = str + + +# ***** get resources for cards ****************************************************************** +def get_material_resources(category='Solid'): + + resources = {} # { resource_path: icon_path, ... } + + # TODO: move GUI preferences from FEM to a new side tab Material + # https://forum.freecadweb.org/viewtopic.php?f=10&t=35515 + mat_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/Resources") + use_built_in_materials = mat_prefs.GetBool("UseBuiltInMaterials", True) + use_mat_from_config_dir = mat_prefs.GetBool("UseMaterialsFromConfigDir", True) + use_mat_from_custom_dir = mat_prefs.GetBool("UseMaterialsFromCustomDir", True) + + if use_built_in_materials: + if category == 'Fluid': + builtin_mat_dir = join( + FreeCAD.getResourceDir(), "Mod", "Material", "FluidMaterial" + ) + + else: + builtin_mat_dir = join( + FreeCAD.getResourceDir(), "Mod", "Material", "StandardMaterial" + ) + resources[builtin_mat_dir] = ":/icons/freecad.svg" + + if use_mat_from_config_dir: + config_mat_dir = join( + FreeCAD.ConfigGet("UserAppData"), "Material" + ) + resources[config_mat_dir] = ":/icons/preferences-general.svg" + + if use_mat_from_custom_dir: + custom_mat_dir = mat_prefs.GetString("CustomMaterialsDir", "") + if os.path.exists(custom_mat_dir): + resources[custom_mat_dir] = ":/icons/user.svg" + else: + FreeCAD.Console.PrintError( + 'Custom material directory set by user: {} does not exist.\n' + .format(custom_mat_dir) + ) + + return resources + + +def output_resources(resources): + FreeCAD.Console.PrintMessage('Directories we gone look for material cards:\n') + for path in resources.keys(): + FreeCAD.Console.PrintMessage(' {}\n'.format(path)) + + +# ***** card handling **************************************************************************** +# used in material editor and FEM material task panels + +def import_materials(category='Solid'): + + resources = get_material_resources(category) + + materials = {} + cards = {} + icons = {} + for path in resources.keys(): + materials, cards, icons = add_cards_from_a_dir( + materials, + cards, + icons, + path, + resources[path] + ) + + return (materials, cards, icons) + + +def add_cards_from_a_dir(materials, cards, icons, mat_dir, icon): + # fill materials and icons + import glob + from importFCMat import read + dir_path_list = glob.glob(mat_dir + '/*' + ".FCMat") + mat_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/Cards") + delete_duplicates = mat_prefs.GetBool("DeleteDuplicates", True) + # duplicates are indicated on equality of mat dict + # TODO if the unit is different two cards would be different too + for a_path in dir_path_list: + mat_dict = read(a_path) + card_name = os.path.splitext(os.path.basename(a_path))[0] + if delete_duplicates is False: + materials[a_path] = mat_dict + i = 1 + while card_name in cards.values(): + if i == 1: + card_name += ('_' + str(i)) + else: + card_name = card_name[:-1] + str(i) + i += 1 + # print(card_name) + cards[a_path] = card_name + icons[a_path] = icon + else: + if mat_dict not in materials.values(): + materials[a_path] = mat_dict + cards[a_path] = card_name + icons[a_path] = icon + + return (materials, cards, icons) + + +def output_trio(trio): + materials, cards, icons = trio + FreeCAD.Console.PrintMessage('\n\n') + for mat_card in materials: + FreeCAD.Console.PrintMessage( + '{} --> {} -->{}\n' + .format(cards[mat_card], mat_card, icons[mat_card]) + ) + FreeCAD.Console.PrintMessage('\n\n') + + +def output_cards(cards): + FreeCAD.Console.PrintMessage('\n\n') + for mat_card in cards: + FreeCAD.Console.PrintMessage('{} --> {}\n'.format(mat_card, cards[mat_card])) + FreeCAD.Console.PrintMessage('\n\n') + + +def output_icons(icons): + FreeCAD.Console.PrintMessage('\n\n') + for mat_card in icons: + FreeCAD.Console.PrintMessage('{} --> {}\n'.format(mat_card, icons[mat_card])) + FreeCAD.Console.PrintMessage('\n\n') + + +def output_materials(materials): + FreeCAD.Console.PrintMessage('\n\n') + for mat_card in materials: + FreeCAD.Console.PrintMessage('{}\n'.format(mat_card)) + output_material_param(materials[mat_card]) + FreeCAD.Console.PrintMessage('\n\n') + + +def output_material_param(mat_dict): + # thus we check for None + if not mat_dict: + FreeCAD.Console.PrintMessage(' empty matdict\n') + else: + for p in mat_dict: + FreeCAD.Console.PrintMessage(' {} --> {}\n'.format(p, mat_dict[p])) + FreeCAD.Console.PrintMessage('\n')