| | |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | __title__ = "FreeCAD material editor" |
| | __author__ = "Yorik van Havre, Bernd Hahnebach" |
| | __url__ = "https://www.freecad.org" |
| |
|
| | import os |
| | from pathlib import PurePath |
| | import sys |
| | from PySide import QtCore, QtGui, QtSvgWidgets |
| |
|
| | import FreeCAD |
| | import FreeCADGui |
| | |
| | from materialtools.cardutils import get_material_preferred_directory, get_material_preferred_save_directory |
| |
|
| |
|
| | |
| | unicode = str |
| |
|
| |
|
| | |
| | |
| | class MaterialEditor: |
| |
|
| | def __init__(self, obj=None, prop=None, material=None, card_path="", category="Solid"): |
| |
|
| | """Initializes, optionally with an object name and a material property |
| | name to edit, or directly with a material dictionary.""" |
| |
|
| | self.obj = obj |
| | self.prop = prop |
| | self.category = category |
| | self.material = material |
| | self.customprops = [] |
| | self.internalprops = [] |
| | self.groups = [] |
| | self.directory = get_material_preferred_directory() |
| | self.save_directory = get_material_preferred_save_directory() |
| | if self.directory is None: |
| | self.directory = FreeCAD.getResourceDir() + "Mod/Material" |
| | self.materials = {} |
| | self.cards = {} |
| | self.icons = {} |
| | self.initialIndex = -1 |
| | self.edited = False |
| | self.card_path = card_path |
| | filePath = os.path.dirname(__file__) + os.sep |
| | self.iconPath = (filePath + "Resources" + os.sep + "icons" + os.sep) |
| |
|
| | |
| | self.widget = FreeCADGui.PySideUic.loadUi(":/ui/materials-editor.ui") |
| | |
| | self.widget.setWindowFlags(self.widget.windowFlags() |
| | & ~QtCore.Qt.WindowContextHelpButtonHint) |
| |
|
| | |
| | param = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material") |
| | width = param.GetInt("MaterialEditorWidth", 441) |
| | height = param.GetInt("MaterialEditorHeight", 626) |
| | self.widget.resize(width, height) |
| |
|
| | |
| | widget = self.widget |
| | buttonURL = widget.ButtonURL |
| | buttonDeleteProperty = widget.ButtonDeleteProperty |
| | buttonAddProperty = widget.ButtonAddProperty |
| | standardButtons = widget.standardButtons |
| | buttonOpen = widget.ButtonOpen |
| | buttonSave = widget.ButtonSave |
| | comboMaterial = widget.ComboMaterial |
| | treeView = widget.treeView |
| |
|
| | |
| | self.widget.PreviewRender = QtSvgWidgets.QSvgWidget(self.iconPath + "preview-rendered.svg") |
| | self.widget.PreviewRender.setMaximumWidth(64) |
| | self.widget.PreviewRender.setMinimumHeight(64) |
| | self.widget.topLayout.addWidget(self.widget.PreviewRender) |
| | self.widget.PreviewVector = QtSvgWidgets.QSvgWidget(self.iconPath + "preview-vector.svg") |
| | self.widget.PreviewVector.setMaximumWidth(64) |
| | self.widget.PreviewVector.setMinimumHeight(64) |
| | self.widget.topLayout.addWidget(self.widget.PreviewVector) |
| | self.updatePreviews(mat=material) |
| |
|
| | buttonURL.setIcon(QtGui.QIcon(":/icons/internet-web-browser.svg")) |
| | buttonDeleteProperty.setEnabled(False) |
| | standardButtons.button(QtGui.QDialogButtonBox.Ok).setAutoDefault(False) |
| | standardButtons.button(QtGui.QDialogButtonBox.Cancel).setAutoDefault(False) |
| | self.updateCardsInCombo() |
| | |
| | |
| |
|
| | standardButtons.rejected.connect(self.reject) |
| | standardButtons.button(QtGui.QDialogButtonBox.Ok).clicked.connect(self.verify) |
| | buttonOpen.clicked.connect(self.openfile) |
| | buttonSave.clicked.connect(self.savefile) |
| | buttonURL.clicked.connect(self.openProductURL) |
| | comboMaterial.currentIndexChanged[int].connect(self.chooseMaterial) |
| | buttonAddProperty.clicked.connect(self.addCustomProperty) |
| | buttonDeleteProperty.clicked.connect(self.deleteCustomProperty) |
| | treeView.clicked.connect(self.onClickTree) |
| |
|
| | model = QtGui.QStandardItemModel() |
| | treeView.setModel(model) |
| | treeView.setUniformRowHeights(True) |
| | treeView.setItemDelegate(MaterialsDelegate()) |
| | model.itemChanged.connect(self.modelChange) |
| |
|
| | |
| | self.implementModel() |
| |
|
| | |
| | matProperty = None |
| | if self.prop and self.obj: |
| | matProperty = FreeCAD.ActiveDocument.getObject(self.obj).getPropertyByName(self.prop) |
| | elif self.material: |
| | matProperty = self.material |
| |
|
| | if matProperty: |
| | self.updateMatParamsInTree(matProperty) |
| | self.widget.ComboMaterial.setCurrentIndex(0) |
| | |
| |
|
| | if self.card_path: |
| | |
| | self.initialIndex = self.widget.ComboMaterial.findData(self.card_path) |
| | self.chooseMaterial(self.initialIndex) |
| |
|
| | |
| | |
| |
|
| | def implementModel(self): |
| |
|
| | """implements the model with the material attribute structure.""" |
| |
|
| | p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material") |
| | widget = self.widget |
| | treeView = widget.treeView |
| | model = treeView.model() |
| | model.setHorizontalHeaderLabels(["Property", "Value", "Type"]) |
| |
|
| | treeView.setColumnWidth(0, 250) |
| | treeView.setColumnWidth(1, 250) |
| | treeView.setColumnHidden(2, True) |
| |
|
| | from materialtools.cardutils import get_material_template |
| | template_data = get_material_template(True) |
| |
|
| | for group in template_data: |
| | gg = list(group)[0] |
| | top = QtGui.QStandardItem(gg) |
| | model.appendRow([top]) |
| | self.groups.append(gg) |
| |
|
| | for properName in group[gg]: |
| | pp = properName |
| | item = QtGui.QStandardItem(pp) |
| | item.setToolTip(group[gg][properName]["Description"]) |
| | self.internalprops.append(pp) |
| |
|
| | it = QtGui.QStandardItem() |
| | it.setToolTip(group[gg][properName]["Description"]) |
| |
|
| | tt = group[gg][properName]["Type"] |
| | itType = QtGui.QStandardItem(tt) |
| |
|
| | top.appendRow([item, it, itType]) |
| | treeView.setExpanded(top.index(), p.GetBool("TreeExpand"+gg, True)) |
| | |
| |
|
| | |
| | self.edited = False |
| |
|
| | def updateMatParamsInTree(self, data): |
| |
|
| | """updates the contents of the editor with the given dictionary |
| | the material property keys where added to the editor already |
| | unknown material property keys will be added to the user defined group""" |
| |
|
| | |
| | model = self.widget.treeView.model() |
| | root = model.invisibleRootItem() |
| | for gg in range(root.rowCount() - 1): |
| | group = root.child(gg, 0) |
| | for pp in range(group.rowCount()): |
| | item = group.child(pp, 0) |
| | it = group.child(pp, 1) |
| | kk = self.collapseKey(item.text()) |
| |
|
| | try: |
| | value = data[kk] |
| | it.setText(value) |
| | del data[kk] |
| | except KeyError: |
| | |
| | |
| | if (kk == "Standard Code") and ("Norm" in data) and data["Norm"]: |
| | it.setText(data["Norm"]) |
| | del data["Norm"] |
| | it.setText("") |
| |
|
| | userGroup = root.child(gg + 1, 0) |
| | userGroup.setRowCount(0) |
| | self.customprops = [] |
| |
|
| | for k, i in data.items(): |
| | k = self.expandKey(k) |
| | item = QtGui.QStandardItem(k) |
| | it = QtGui.QStandardItem(i) |
| | userGroup.appendRow([item, it]) |
| | self.customprops.append(k) |
| | self.edited = False |
| |
|
| | def chooseMaterial(self, index): |
| | if index < 0: |
| | return |
| |
|
| | if self.verifyMaterial(): |
| | """ |
| | Save any unchanged data |
| | """ |
| | self.edited = False |
| | else: |
| | return |
| |
|
| | self.card_path = self.widget.ComboMaterial.itemData(index) |
| | FreeCAD.Console.PrintMessage( |
| | "choose_material in material editor:\n" |
| | " {}\n".format(self.card_path) |
| | ) |
| | if os.path.isfile(self.card_path): |
| | from importFCMat import read |
| | d = read(self.card_path) |
| | self.updateMatParamsInTree(d) |
| | |
| | |
| | self.widget.ComboMaterial.setCurrentIndex(index) |
| | else: |
| | FreeCAD.Console.PrintError("Material card not found: {}\n".format(self.card_path)) |
| |
|
| | def verifyMaterial(self): |
| | if self.edited: |
| | reply = QtGui.QMessageBox.question(self.widget, |
| | translate("Material","The document has been modified."), |
| | translate("Material","Save changes?"), |
| | QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | QtGui.QMessageBox.Cancel, |
| | QtGui.QMessageBox.Save) |
| |
|
| | if reply == QtGui.QMessageBox.Cancel: |
| | return False |
| | if reply == QtGui.QMessageBox.Save: |
| | self.savefile() |
| |
|
| | return True |
| |
|
| | def updateCardsInCombo(self): |
| |
|
| | """updates the contents of the materials combo with existing material cards""" |
| |
|
| | mat_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material/Cards") |
| | sort_by_resources = mat_prefs.GetBool("SortByResources", False) |
| |
|
| | |
| | from materialtools.cardutils import import_materials as getmats |
| | self.materials, self.cards, self.icons = getmats(category=self.category) |
| |
|
| | card_name_list = [] |
| |
|
| | 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]]) |
| |
|
| | card_name_list.insert(0, [None, "", ""]) |
| | self.widget.ComboMaterial.clear() |
| | for mat in card_name_list: |
| | icon_data = mat[2] |
| |
|
| | if icon_data and isinstance(icon_data, bytes): |
| | byte_array = QtCore.QByteArray(icon_data) |
| | pixmap = QtGui.QPixmap() |
| | if pixmap.loadFromData(byte_array): |
| | icon = QtGui.QIcon(pixmap) |
| | else: |
| | icon = QtGui.QIcon() |
| | elif isinstance(icon_data, str) and icon_data: |
| | |
| | icon = QtGui.QIcon(icon_data) |
| | else: |
| | |
| | icon = QtGui.QIcon() |
| |
|
| | self.widget.ComboMaterial.addItem(icon, mat[0], mat[1]) |
| |
|
| | def openProductURL(self): |
| |
|
| | """opens the contents of the ProductURL field in an external browser.""" |
| |
|
| | model = self.widget.treeView.model() |
| | item = model.findItems(translate("Material", "Product URL"), |
| | QtCore.Qt.MatchRecursive, 0)[0] |
| | group = item.parent() |
| | it = group.child(item.row(), 1) |
| | url = it.text() |
| | if url: |
| | QtGui.QDesktopServices.openUrl(QtCore.QUrl(url, QtCore.QUrl.TolerantMode)) |
| |
|
| | def modelChange(self, item): |
| | """ |
| | Called when an item in the tree is modified. This will set edited to True, but this |
| | will be reset in the event of mass updates, such as loading a card |
| | """ |
| | self.edited = True |
| |
|
| | def verify(self, button): |
| | """ |
| | Verify that the user wants to save any changed data before exiting |
| | """ |
| |
|
| | if self.edited: |
| | reply = QtGui.QMessageBox.question(self.widget, |
| | translate("Material","The document has been modified."), |
| | translate("Material","Save changes?"), |
| | QtGui.QMessageBox.Save | QtGui.QMessageBox.Discard | QtGui.QMessageBox.Cancel, |
| | QtGui.QMessageBox.Save) |
| |
|
| | if reply == QtGui.QMessageBox.Cancel: |
| | return |
| | if reply == QtGui.QMessageBox.Save: |
| | self.savefile() |
| |
|
| | self.accept() |
| |
|
| | def accept(self): |
| | "" |
| |
|
| | self.storeSize() |
| | QtGui.QDialog.accept(self.widget) |
| |
|
| | def reject(self): |
| | "" |
| |
|
| | self.storeSize() |
| | QtGui.QDialog.reject(self.widget) |
| |
|
| | def storeSize(self): |
| | "stores the widget size" |
| | |
| | p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material") |
| | p.SetInt("MaterialEditorWidth", self.widget.width()) |
| | p.SetInt("MaterialEditorHeight", self.widget.height()) |
| | root = self.widget.treeView.model().invisibleRootItem() |
| | for gg in range(root.rowCount()): |
| | group = root.child(gg) |
| | p.SetBool("TreeExpand"+group.text(), self.widget.treeView.isExpanded(group.index())) |
| |
|
| | def expandKey(self, key): |
| | "adds spaces before caps in a KeyName" |
| | nk = "" |
| | for ln in key: |
| | if ln.isupper(): |
| | if nk: |
| | |
| | if not nk[-1].isupper(): |
| | nk += " " |
| | nk += ln |
| | return nk |
| |
|
| | def collapseKey(self, key): |
| | "removes the spaces in a Key Name" |
| | nk = "" |
| | for ln in key: |
| | if ln != " ": |
| | nk += ln |
| | return nk |
| |
|
| | def addCustomProperty(self, key=None, value=None): |
| | "Adds a custom property to the editor, optionally with a value." |
| |
|
| | if not key: |
| | key = self.widget.EditProperty.text() |
| |
|
| | if key: |
| | model = self.widget.treeView.model() |
| | item = model.findItems(key, QtCore.Qt.MatchRecursive, 0) |
| | if not item: |
| |
|
| | top = model.findItems(translate("Material", "UserDefined"), |
| | QtCore.Qt.MatchExactly, 0)[0] |
| | item = QtGui.QStandardItem(key) |
| | it = QtGui.QStandardItem(value) |
| | top.appendRow([item, it]) |
| | self.customprops.append(key) |
| | self.edited = True |
| |
|
| | def deleteCustomProperty(self, key=None): |
| |
|
| | """Deletes a custom property from the editor, |
| | or deletes the value of an internal property.""" |
| |
|
| | widget = self.widget |
| | treeView = widget.treeView |
| | model = treeView.model() |
| | buttonDeleteProperty = widget.ButtonDeleteProperty |
| |
|
| | if not key: |
| |
|
| | index = treeView.selectedIndexes()[0] |
| | item = model.itemFromIndex(index) |
| | key = item.text() |
| |
|
| | if key: |
| | item = model.findItems(key, QtCore.Qt.MatchRecursive, 0) |
| | if item: |
| |
|
| | index = model.indexFromItem(item[0]) |
| | topIndex = index.parent() |
| | top = model.itemFromIndex(topIndex) |
| | row = item[0].row() |
| |
|
| | if key in self.customprops: |
| | top.takeRow(row) |
| | self.customprops.remove(key) |
| | buttonDeleteProperty.setProperty("text", "Delete property") |
| |
|
| | elif key in self.internalprops: |
| | it = top.child(row, 1) |
| | it.setText("") |
| | buttonDeleteProperty.setProperty("text", "Delete value") |
| |
|
| | self.edited = True |
| |
|
| | buttonDeleteProperty.setEnabled(False) |
| |
|
| | def onClickTree(self, index): |
| |
|
| | """Checks if the current item is a custom or an internal property, |
| | and enable the delete property or delete value button.""" |
| |
|
| | widget = self.widget |
| | buttonDeleteProperty = widget.ButtonDeleteProperty |
| | treeView = widget.treeView |
| | model = treeView.model() |
| | ind = treeView.selectedIndexes()[0] |
| | item = model.itemFromIndex(ind) |
| | text = item.text() |
| |
|
| | if text in self.customprops: |
| | buttonDeleteProperty.setEnabled(True) |
| | buttonDeleteProperty.setProperty("text", "Delete property") |
| |
|
| | elif text in self.internalprops: |
| | indParent = ind.parent() |
| | group = model.itemFromIndex(indParent) |
| | row = item.row() |
| | it = group.child(row, 1) |
| | buttonDeleteProperty.setProperty("text", "Delete value") |
| | if it.text(): |
| | buttonDeleteProperty.setEnabled(True) |
| | else: |
| | buttonDeleteProperty.setEnabled(False) |
| |
|
| | else: |
| | buttonDeleteProperty.setEnabled(False) |
| | buttonDeleteProperty.setProperty("text", "Delete property") |
| |
|
| | self.updatePreviews() |
| |
|
| | def getDict(self): |
| | "returns a dictionary from the contents of the editor." |
| |
|
| | model = self.widget.treeView.model() |
| | if model is None: |
| | return {} |
| | root = model.invisibleRootItem() |
| |
|
| | d = {} |
| | for gg in range(root.rowCount()): |
| | group = root.child(gg) |
| | for row in range(group.rowCount()): |
| | kk = group.child(row, 0).text() |
| | ii = group.child(row, 1).text() |
| |
|
| | |
| | |
| | matkey = self.collapseKey(str(kk)) |
| | matvalue = unicode(ii) |
| | if matvalue or (matkey == "Name"): |
| | |
| | d[matkey] = matvalue |
| | |
| | return d |
| |
|
| | def outputDict(self, d): |
| | print("MaterialEditor dictionary") |
| | for param in d: |
| | print(" {} : {}".format(param, d[param])) |
| |
|
| | """ |
| | def setTexture(self, pattern): |
| | "displays a texture preview if needed" |
| | self.widget.PreviewVector.hide() |
| | if pattern: |
| | try: |
| | import DrawingPatterns |
| | except: |
| | print("DrawingPatterns not found") |
| | else: |
| | pattern = DrawingPatterns.buildFileSwatch(pattern, size=96, png=True) |
| | if pattern: |
| | self.widget.PreviewVector.setPixmap(QtGui.QPixmap(pattern)) |
| | self.widget.PreviewVector.show() |
| | """ |
| |
|
| | def updatePreviews(self, mat=None): |
| | "updates the preview images from the content of the editor" |
| | if not mat: |
| | mat = self.getDict() |
| | diffcol = None |
| | highlightcol = None |
| | sectioncol = None |
| | if "DiffuseColor" in mat: |
| | diffcol = mat["DiffuseColor"] |
| | elif "ViewColor" in mat: |
| | diffcol = mat["ViwColor"] |
| | elif "Color" in mat: |
| | diffcol = mat["Color"] |
| | if "SpecularColor" in mat: |
| | highlightcol = mat["SpecularColor"] |
| | if "SectionColor" in mat: |
| | sectioncol = mat["SectionColor"] |
| | if diffcol or highlightcol: |
| | fd = QtCore.QFile(self.iconPath + "preview-rendered.svg") |
| | if fd.open(QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Text): |
| | svg = QtCore.QTextStream(fd).readAll() |
| | fd.close() |
| | if diffcol: |
| | svg = svg.replace("#d3d7cf", self.getColorHash(diffcol, val=255)) |
| | svg = svg.replace("#555753", self.getColorHash(diffcol, val=125)) |
| | if highlightcol: |
| | svg = svg.replace("#fffffe", self.getColorHash(highlightcol, val=255)) |
| | self.widget.PreviewRender.load(QtCore.QByteArray(bytes(svg, encoding="utf8"))) |
| | if diffcol or sectioncol: |
| | fd = QtCore.QFile(self.iconPath + "preview-vector.svg") |
| | if fd.open(QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Text): |
| | svg = QtCore.QTextStream(fd).readAll() |
| | fd.close() |
| | if diffcol: |
| | svg = svg.replace("#d3d7cf", self.getColorHash(diffcol, val=255)) |
| | svg = svg.replace("#555753", self.getColorHash(diffcol, val=125)) |
| | if sectioncol: |
| | svg = svg.replace("#ffffff", self.getColorHash(sectioncol, val=255)) |
| | self.widget.PreviewVector.load(QtCore.QByteArray(bytes(svg, encoding="utf8"))) |
| |
|
| | def getColorHash(self, col, val=255): |
| | "returns a '#000000' string from a '(0.1,0.2,0.3)' string" |
| | col = [float(x.strip()) for x in col.strip("()").split(",")] |
| | color = QtGui.QColor(int(col[0]*val), int(col[1]*val), int(col[2]*val)) |
| | return color.name() |
| |
|
| | def openfile(self): |
| | if self.verifyMaterial(): |
| | """ |
| | Save any unchanged data |
| | """ |
| | self.edited = False |
| | else: |
| | return |
| |
|
| | "Opens a FCMat file" |
| | if self.category == "Solid": |
| | directory = self.directory + os.sep + "StandardMaterial" |
| | else: |
| | directory = self.directory + os.sep + "FluidMaterial" |
| | if self.card_path is None or len(self.card_path) == 0: |
| | self.card_path = directory |
| | filetuple = QtGui.QFileDialog.getOpenFileName( |
| | QtGui.QApplication.activeWindow(), |
| | "Open FreeCAD material file", |
| | self.card_path, |
| | "*.FCMat" |
| | ) |
| | if os.path.isfile(filetuple[0]): |
| | card_path_new = filetuple[0] |
| | else: |
| | FreeCAD.Console.PrintError("Error: Invalid path to the material file\n") |
| | return |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | if self.widget.ComboMaterial.count() > 1: |
| | path = self.widget.ComboMaterial.itemData(1) |
| | |
| | |
| | testBackslash = path.rsplit('\\', 1)[0] |
| | testSlash = path.rsplit('/', 1)[0] |
| | if testBackslash == path: |
| | path = testBackslash.rsplit('/', 1)[0] + '/' |
| | elif testSlash == path: |
| | path = testSlash.rsplit('\\', 1)[0] + '\\' |
| | |
| | |
| | else: |
| | pathBackslash = path.rsplit('\\', 1)[0] |
| | pathSlash = path.rsplit('/', 1)[0] |
| | if len(pathBackslash) > len(pathSlash): |
| | path = pathBackslash + '\\' |
| | else: |
| | path = pathSlash + '/' |
| | |
| | testBackslash = card_path_new.rsplit('\\', 1)[0] |
| | if testBackslash == card_path_new: |
| | self.card_path = path + card_path_new.rsplit('/', 1)[1] |
| | else: |
| | self.card_path = path + card_path_new.rsplit('\\', 1)[1] |
| | index = self.widget.ComboMaterial.findData(self.card_path) |
| |
|
| | |
| | |
| | if index == -1: |
| | FreeCAD.Console.PrintMessage( |
| | "Card path: {} not found in known cards.\n" |
| | "The material parameter only are loaded.\n" |
| | .format(self.card_path) |
| | ) |
| | |
| | self.card_path = card_path_new |
| | from importFCMat import read |
| | materialDict = read(self.card_path) |
| | if materialDict: |
| | self.updateMatParamsInTree(materialDict) |
| | self.widget.ComboMaterial.setCurrentIndex(0) |
| | |
| | else: |
| | self.chooseMaterial(index) |
| | self.directory = os.path.dirname(self.card_path) |
| |
|
| | def savefile(self): |
| | "Saves a FCMat file." |
| |
|
| | model = self.widget.treeView.model() |
| | item = model.findItems(translate("Material", "Name"), |
| | QtCore.Qt.MatchRecursive, 0)[0] |
| | group = item.parent() |
| | it = group.child(item.row(), 1) |
| | name = it.text() |
| | if not name: |
| | name = "Material" |
| | filetuple = QtGui.QFileDialog.getSaveFileName( |
| | QtGui.QApplication.activeWindow(), |
| | "Save FreeCAD material file", |
| | self.save_directory + "/" + name + ".FCMat", |
| | "*.FCMat" |
| | ) |
| | |
| | filename = filetuple[0] |
| | if filename: |
| | |
| | self.save_directory = os.path.dirname(filename) |
| | self.directory = self.save_directory |
| | self.card_path = filename |
| |
|
| | d = self.getDict() |
| | |
| | if d: |
| | |
| | path = PurePath(filename) |
| | d["CardName"] = path.stem |
| |
|
| | from importFCMat import write |
| | write(filename, d) |
| | import Materials |
| | |
| | manager = Materials.MaterialManager() |
| | manager.getMaterialByPath(filename) |
| | self.edited = False |
| | self.updateCardsInCombo() |
| |
|
| | |
| | self.widget.ComboMaterial.setCurrentText(path.stem) |
| |
|
| | def show(self): |
| | return self.widget.show() |
| |
|
| | def exec_(self): |
| | return self.widget.exec_() |
| |
|
| |
|
| | |
| | |
| | class MaterialsDelegate(QtGui.QStyledItemDelegate): |
| |
|
| | """provides display and editing facilities for data items from a model.""" |
| |
|
| | def __init__(self): |
| | "" |
| |
|
| | self.matproperty = "" |
| | self.Value = "" |
| | super(MaterialsDelegate, self).__init__() |
| |
|
| | def createEditor(self, parent, option, index): |
| |
|
| | """returns the widget used to change data from the model.""" |
| |
|
| | model = index.model() |
| | column = index.column() |
| |
|
| | item = model.itemFromIndex(index) |
| | group = item.parent() |
| | if not group: |
| | return |
| |
|
| | if column == 1: |
| | row = index.row() |
| |
|
| | PP = group.child(row, 0) |
| | self.matproperty = PP.text().replace(" ", "") |
| |
|
| | TT = group.child(row, 2) |
| | if TT: |
| | Type = TT.text() |
| | else: |
| | Type = "String" |
| |
|
| | VV = group.child(row, 1) |
| | self.Value = VV.text() |
| |
|
| | editor = matProperWidget(parent, self.matproperty, Type, self.Value) |
| |
|
| | elif column == 0: |
| | if group.text() == "User defined": |
| | editor = matProperWidget(parent) |
| | else: |
| | return |
| |
|
| | else: |
| | return |
| |
|
| | return editor |
| |
|
| | def setEditorData(self, editor, index): |
| |
|
| | """provides the widget with data to manipulate.""" |
| |
|
| | Type = editor.property("Type") |
| | model = index.model() |
| | item = model.itemFromIndex(index) |
| |
|
| | if Type == "Color": |
| | color = editor.property("color") |
| | color = tuple([v/255.0 for v in color.getRgb()]) |
| | item.setText(str(color)) |
| |
|
| | elif Type == "File": |
| | lineEdit = editor.children()[1] |
| | item.setText(lineEdit.text()) |
| |
|
| | elif Type == "Float": |
| | |
| | inputValue = float('%.6g' % editor.value()) |
| | item.setText(str(inputValue)) |
| |
|
| | elif Type == "Quantity": |
| | if not hasattr(FreeCAD.Units, self.matproperty): |
| | FreeCAD.Console.PrintError( |
| | "Error: property '{}' is a quantity but has no unit defined\n" |
| | .format(self.matproperty) |
| | ) |
| | return |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | if not self.Value: |
| | |
| | unit = getattr(FreeCAD.Units, self.matproperty) |
| | quantity = FreeCAD.Units.Quantity(1, unit) |
| | item.setText(str(editor.value()) + " " + quantity.getUserPreferred()[2]) |
| | else: |
| | |
| | item.setText(str(editor.value()) + " " + self.Value.split()[1]) |
| |
|
| | else: |
| | super(MaterialsDelegate, self).setEditorData(editor, index) |
| |
|
| |
|
| | |
| | |
| | def matProperWidget(parent=None, matproperty=None, Type="String", Value=None, |
| | minimum=None, maximum=None, stepsize=None, precision=12): |
| |
|
| | """customs widgets for the material stuff.""" |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| |
|
| | ui = FreeCADGui.UiLoader() |
| |
|
| | if Type == "String": |
| | widget = ui.createWidget("Gui::PrefLineEdit") |
| |
|
| | elif Type == "URL": |
| | widget = ui.createWidget("Gui::PrefLineEdit") |
| |
|
| | elif Type == "File": |
| | widget = ui.createWidget("Gui::FileChooser") |
| | if Value: |
| | lineEdit = widget.children()[1] |
| | lineEdit.setText(Value) |
| |
|
| | elif Type == "Quantity": |
| | |
| | |
| | |
| | |
| | |
| | widget = ui.createWidget("Gui::DoubleSpinBox") |
| |
|
| | if minimum is None: |
| | widget.setMinimum(-1*sys.float_info.max) |
| | else: |
| | widget.setMinimum(minimum) |
| | if maximum is None: |
| | widget.setMaximum(sys.float_info.max) |
| | else: |
| | widget.setMaximum(maximum) |
| |
|
| | |
| | |
| | widget.setDecimals(6) |
| |
|
| | |
| | |
| | |
| | |
| | import re |
| | if re.search(r'\d', matproperty): |
| | matpropertyNum = matproperty.rstrip('0123456789') |
| | matproperty = matpropertyNum |
| |
|
| | if Value: |
| | widget.setValue(float(Value.split()[0])) |
| |
|
| | elif Type == "Integer": |
| | widget = ui.createWidget("Gui::UIntSpinBox") |
| | if minimum is None: |
| | widget.setMinimum(0) |
| | else: |
| | widget.setMinimum(minimum) |
| | if maximum is None: |
| | widget.setMaximum(sys.maxsize) |
| | else: |
| | widget.setMaximum(maximum) |
| |
|
| | elif Type == "Float": |
| | widget = ui.createWidget("Gui::DoubleSpinBox") |
| | |
| | |
| | widget.setDecimals(6) |
| | |
| | widget.setSingleStep(0.1) |
| | if minimum is None: |
| | widget.setMinimum(sys.float_info.min) |
| | else: |
| | widget.setMinimum(minimum) |
| | if maximum is None: |
| | widget.setMaximum(sys.float_info.max) |
| | else: |
| | widget.setMaximum(maximum) |
| | widget.setValue(float(Value)) |
| |
|
| | elif Type == "Enumerator": |
| | widget = ui.createWidget("Gui::PrefComboBox") |
| |
|
| | elif Type == "Boolean": |
| | widget = ui.createWidget("Gui::PrefComboBox") |
| | widget.insertItems(0, ["", "False", "True"]) |
| |
|
| | elif Type == "Vector": |
| | widget = ui.createWidget("Gui::PrefLineEdit") |
| |
|
| | elif Type == "Color": |
| | widget = ui.createWidget("Gui::PrefColorButton") |
| | if Value: |
| | value = string2tuple(Value) |
| | color = QtGui.QColor() |
| | color.setRgb(value[0], value[1], value[2], value[3]) |
| | widget.setProperty("color", color) |
| |
|
| | else: |
| | widget = QtGui.QLineEdit() |
| |
|
| | widget.setProperty("Type", Type) |
| |
|
| | widget.setParent(parent) |
| |
|
| | return widget |
| |
|
| |
|
| | def string2tuple(string): |
| | "provisionally" |
| | value = string[1:-1] |
| | value = value.split(",") |
| | value = [int(float(v)*255) for v in value] |
| | value = tuple(value) |
| | return value |
| |
|
| |
|
| | def translate(context, text): |
| | "translates text" |
| | return text |
| |
|
| |
|
| | def openEditor(obj=None, prop=None): |
| | """openEditor([obj,prop]): opens the editor, optionally with |
| | an object name and material property name to edit""" |
| | |
| | |
| | |
| | |
| | |
| | |
| | FreeCADGui.runCommand('Material_Edit',0) |
| |
|
| |
|
| | def editMaterial(material=None, card_path=None, category="Solid"): |
| | """editMaterial(material): opens the editor to edit the contents |
| | of the given material dictionary. Returns the modified material dictionary.""" |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | editor = MaterialEditor(material=material, card_path=card_path, category=category) |
| | result = editor.exec_() |
| | if result: |
| | return editor.getDict() |
| | else: |
| | |
| | return {} |
| |
|
| |
|
| | |
| | |
| | """ |
| | # some examples how to open the material editor in Python: |
| | import MaterialEditor |
| | MaterialEditor.openEditor() |
| | |
| | doc = FreeCAD.open( |
| | FreeCAD.ConfigGet("AppHomePath") + "data/examples/FEMExample.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" |
| | }) |
| | |
| | import MaterialEditor |
| | MaterialEditor.editMaterial("ABS") |
| | |
| | """ |
| |
|