Material: Mateiral editor UI fixes

- Fixed color buttons
- Introduced "Section Color" property
- Fixed named properties
- Added preview icons
- Remember UI dimensions and expanded states
This commit is contained in:
Yorik van Havre
2020-10-27 19:16:58 +01:00
parent 6fda0a2faa
commit cb245845c5
8 changed files with 260 additions and 167 deletions

View File

@@ -516,16 +516,17 @@ class _ArchMaterialTaskPanel:
self.existingmaterials = []
self.obj = obj
self.form = FreeCADGui.PySideUic.loadUi(":/ui/ArchMaterial.ui")
self.color = QtGui.QColor(128,128,128)
colorPix = QtGui.QPixmap(16,16)
colorPix.fill(self.color)
colorPix.fill(QtGui.QColor(204,204,204))
self.form.ButtonColor.setIcon(QtGui.QIcon(colorPix))
self.form.ButtonSectionColor.setIcon(QtGui.QIcon(colorPix))
self.form.ButtonUrl.setIcon(QtGui.QIcon(":/icons/internet-web-browser.svg"))
QtCore.QObject.connect(self.form.comboBox_MaterialsInDir, QtCore.SIGNAL("currentIndexChanged(QString)"), self.chooseMat)
QtCore.QObject.connect(self.form.comboBox_FromExisting, QtCore.SIGNAL("currentIndexChanged(int)"), self.fromExisting)
QtCore.QObject.connect(self.form.comboFather, QtCore.SIGNAL("currentIndexChanged(QString)"), self.setFather)
QtCore.QObject.connect(self.form.comboFather, QtCore.SIGNAL("currentTextChanged(QString)"), self.setFather)
QtCore.QObject.connect(self.form.ButtonColor,QtCore.SIGNAL("pressed()"),self.getColor)
QtCore.QObject.connect(self.form.ButtonSectionColor,QtCore.SIGNAL("pressed()"),self.getSectionColor)
QtCore.QObject.connect(self.form.ButtonUrl,QtCore.SIGNAL("pressed()"),self.openUrl)
QtCore.QObject.connect(self.form.ButtonEditor,QtCore.SIGNAL("pressed()"),self.openEditor)
QtCore.QObject.connect(self.form.ButtonCode,QtCore.SIGNAL("pressed()"),self.getCode)
@@ -551,21 +552,14 @@ class _ArchMaterialTaskPanel:
self.form.FieldName.setText(self.obj.Label)
if 'Description' in self.material:
self.form.FieldDescription.setText(self.material['Description'])
col = None
if 'DiffuseColor' in self.material:
col = self.material["DiffuseColor"]
self.form.ButtonColor.setIcon(self.getColorIcon(self.material["DiffuseColor"]))
elif 'ViewColor' in self.material:
col = self.material["ViewColor"]
self.form.ButtonColor.setIcon(self.getColorIcon(self.material["ViewColor"]))
elif 'Color' in self.material:
col = self.material["Color"]
if col:
if "(" in col:
c = tuple([float(f) for f in col.strip("()").split(",")])
self.color = QtGui.QColor()
self.color.setRgbF(c[0],c[1],c[2])
colorPix = QtGui.QPixmap(16,16)
colorPix.fill(self.color)
self.form.ButtonColor.setIcon(QtGui.QIcon(colorPix))
self.form.ButtonColor.setIcon(self.getColorIcon(self.material["Color"]))
if 'SectionColor' in self.material:
self.form.ButtonSectionColor.setIcon(self.getColorIcon(self.material["SectionColor"]))
if 'StandardCode' in self.material:
self.form.FieldCode.setText(self.material['StandardCode'])
if 'ProductURL' in self.material:
@@ -589,18 +583,35 @@ class _ArchMaterialTaskPanel:
self.form.comboFather.addItem(father)
self.form.comboFather.setCurrentIndex(self.form.comboFather.count()-1)
def getColorIcon(self,color):
if color:
if "(" in color:
c = tuple([float(f) for f in color.strip("()").split(",")])
qcolor = QtGui.QColor()
qcolor.setRgbF(c[0],c[1],c[2])
colorPix = QtGui.QPixmap(16,16)
colorPix.fill(qcolor)
icon = QtGui.QIcon(colorPix)
return icon
return QtGui.QIcon()
def getFields(self):
"sets self.material from the contents of the task box"
self.material['Name'] = self.form.FieldName.text()
self.material['Description'] = self.form.FieldDescription.text()
self.material['DiffuseColor'] = str(self.color.getRgbF()[:3])
self.material['DiffuseColor'] = self.getColorFromIcon(self.form.ButtonColor.icon())
self.material['ViewColor'] = self.material['DiffuseColor']
self.material['Color'] = self.material['DiffuseColor']
self.material['SectionColor'] = self.getColorFromIcon(self.form.ButtonSectionColor.icon())
self.material['StandardCode'] = self.form.FieldCode.text()
self.material['ProductURL'] = self.form.FieldUrl.text()
self.material['Transparency'] = str(self.form.SpinBox_Transparency.value())
def getColorFromIcon(self,icon):
"gets pixel color from the given icon"
pixel = icon.pixmap(16,16).toImage().pixel(0,0)
return str(QtGui.QColor(pixel).getRgbF())
def accept(self):
self.getFields()
if self.obj:
@@ -635,11 +646,18 @@ class _ArchMaterialTaskPanel:
def getColor(self):
"opens a color picker dialog"
self.color = QtGui.QColorDialog.getColor()
color = QtGui.QColorDialog.getColor()
colorPix = QtGui.QPixmap(16,16)
colorPix.fill(self.color)
colorPix.fill(color)
self.form.ButtonColor.setIcon(QtGui.QIcon(colorPix))
def getSectionColor(self):
"opens a color picker dialog"
color = QtGui.QColorDialog.getColor()
colorPix = QtGui.QPixmap(16,16)
colorPix.fill(color)
self.form.ButtonSectionColor.setIcon(QtGui.QIcon(colorPix))
def fillMaterialCombo(self):
"fills the combo with the existing FCMat cards"
# look for cards in both resources dir and a Materials sub-folder in the user folder.

View File

@@ -131,6 +131,30 @@
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLabel" name="label_8">
<property name="text">
<string>Section Color</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ButtonSectionColor">
<property name="maximumSize">
<size>
<width>30</width>
<height>16777215</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>

View File

@@ -26,11 +26,11 @@ __url__ = "http://www.freecadweb.org"
import os
import sys
from PySide import QtCore, QtGui
# from PySide import QtUiTools, QtSvg
from PySide import QtCore, QtGui, QtSvg
import FreeCAD
import FreeCADGui
import Material_rc
# is this still needed after the move to card utils???
if sys.version_info.major >= 3:
@@ -61,6 +61,14 @@ class MaterialEditor:
os.path.dirname(__file__) + os.sep + "materials-editor.ui"
)
self.widget.setWindowIcon(QtGui.QIcon(":/icons/preview-rendered.svg"))
# restore size and position
p = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Material")
w = p.GetInt("MaterialEditorWidth",441)
h = p.GetInt("MaterialEditorHeight",626)
self.widget.resize(w,h)
# additional UI fixes and tweaks
widget = self.widget
buttonURL = widget.ButtonURL
@@ -72,9 +80,17 @@ class MaterialEditor:
comboMaterial = widget.ComboMaterial
treeView = widget.treeView
# temporarily hide preview fields, as they are not used yet
# TODO : implement previews
widget.PreviewGroup.hide()
# create preview svg slots
self.widget.PreviewRender = QtSvg.QSvgWidget(":/icons/preview-rendered.svg")
self.widget.PreviewRender.setMaximumWidth(64)
self.widget.PreviewRender.setMinimumHeight(64)
self.widget.topLayout.addWidget(self.widget.PreviewRender)
self.widget.PreviewVector = QtSvg.QSvgWidget(":/icons/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)
@@ -92,7 +108,7 @@ class MaterialEditor:
comboMaterial.currentIndexChanged[int].connect(self.chooseMaterial)
buttonAddProperty.clicked.connect(self.addCustomProperty)
buttonDeleteProperty.clicked.connect(self.deleteCustomProperty)
treeView.clicked.connect(self.checkDeletable)
treeView.clicked.connect(self.onClickTree)
model = QtGui.QStandardItemModel()
treeView.setModel(model)
@@ -126,6 +142,7 @@ class MaterialEditor:
'''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()
@@ -147,18 +164,20 @@ class MaterialEditor:
for properName in group[gg]:
pp = properName # property name
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))
#top.sortChildren(0)
top.sortChildren(0)
treeView.expandAll()
#treeView.expandAll()
def updateMatParamsInTree(self, data):
@@ -181,6 +200,11 @@ class MaterialEditor:
it.setText(value)
del data[kk]
except KeyError:
# treat here changes in Material Card Template
# Norm -> StandardCode
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)
@@ -195,6 +219,7 @@ class MaterialEditor:
self.customprops.append(k)
def chooseMaterial(self, index):
if index < 0:
return
self.card_path = self.widget.ComboMaterial.itemData(index)
@@ -256,13 +281,26 @@ class MaterialEditor:
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"
# store widths
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 = ""
@@ -338,7 +376,7 @@ class MaterialEditor:
buttonDeleteProperty.setEnabled(False)
def checkDeletable(self, index):
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.'''
@@ -370,6 +408,8 @@ class MaterialEditor:
buttonDeleteProperty.setEnabled(False)
buttonDeleteProperty.setProperty("text", "Delete property")
self.updatePreviews()
def getDict(self):
"returns a dictionary from the contents of the editor."
@@ -414,6 +454,52 @@ class MaterialEditor:
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(":/icons/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(":/icons/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):
"Opens a FCMat file"
filetuple = QtGui.QFileDialog.getOpenFileName(
@@ -550,7 +636,7 @@ class MaterialsDelegate(QtGui.QStyledItemDelegate):
if Type == "Color":
color = editor.property('color')
color = color.getRgb()
color = tuple([v/255.0 for v in color.getRgb()])
item.setText(str(color))
elif Type == "File":
@@ -650,7 +736,7 @@ def string2tuple(string):
"provisionally"
value = string[1:-1]
value = value.split(',')
value = [int(v) for v in value]
value = [int(float(v)*255) for v in value]
value = tuple(value)
return value

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@@ -134,7 +134,7 @@
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
style="fill:#babdb6;stroke:#d3d7cf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
style="fill:#555753;stroke:#555753;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 59.035745,9.6627807 59.108006,34.818961 40.91531,54.264958 42.698526,19.372964 z"
id="path3022"
inkscape:connector-curvature="0"

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 5.7 KiB

View File

@@ -39,56 +39,52 @@
CardName: # first in group
Type: 'String'
URL: ''
Description: " "
Description: "This is the name under which this material should be saved as a file"
AuthorAndLicense:
Type: 'String'
URL: ''
Description: " "
Description: "Optional author name and license information"
- General:
Name: # first in group
Type: 'String'
URL: ''
Description: "General name" # used to be equal with card name, TODO: should all cards be changed back to this?
NameDE:
Type: 'String'
URL: ''
Description: "German localized name"
NamePL:
Type: 'String'
URL: ''
Description: "Polish localized name"
# NameDE:
# Type: 'String'
# URL: ''
# Description: "German localized name"
# NamePL:
# Type: 'String'
# URL: ''
# Description: "Polish localized name"
Description:
Type: 'String'
URL: ''
Description: "A more elaborate description of the material"
DescriptionDE:
Type: 'String'
URL: ''
Description: "German localized description"
DescriptionPL:
Type: 'String'
URL: ''
Description: "Polish localized description"
# DescriptionDE:
# Type: 'String'
# URL: ''
# Description: "German localized description"
# DescriptionPL:
# Type: 'String'
# URL: ''
# Description: "Polish localized description"
Father:
Type: 'String'
URL: ''
Description: "Father of the material, i.e. for PLA is Thermoplast, for Steel is Metal"
Description: "Father of the material, ex. the father of PLA is Thermoplast, the father of Steel is Metal"
KindOfMaterial:
Type: 'String'
URL: ''
Description: " "
KindOfMaterialDE:
Type: 'String'
URL: ''
Description: "Germand localized kind of material"
# KindOfMaterialDE:
# Type: 'String'
# URL: ''
# Description: "Germand localized kind of material"
MaterialNumber:
Type: 'String'
URL: ''
Description: " "
Norm: # should be merged with StandardCode
Type: 'String'
URL: ''
Description: " "
ReferenceSource:
Type: 'String'
URL: ''
@@ -100,7 +96,7 @@
StandardCode:
Type: 'String'
URL: ''
Description: " "
Description: "This is a reference to a norm or standards system where this material is referenced by a precise ID or number"
- Mechanical:
AngleOfFriction:
Type: 'Quantity'
@@ -245,6 +241,10 @@
Type: 'Float'
URL: ''
Description: " "
SectionColor:
Type: 'Color'
URL: ''
Description: " "
ViewColor:
Type: 'Color'
URL: ''

View File

@@ -23,110 +23,70 @@
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<layout class="QHBoxLayout" name="topLayout">
<item>
<widget class="QPushButton" name="ButtonURL">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>Opens the Product URL of this material in an external browser</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="ComboMaterial">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Existing material cards</string>
</property>
</widget>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QPushButton" name="ButtonURL">
<property name="maximumSize">
<size>
<width>22</width>
<height>22</height>
</size>
</property>
<property name="toolTip">
<string>Opens the Product URL of this material in an external browser</string>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="ComboMaterial">
<property name="minimumSize">
<size>
<width>120</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>Existing material cards</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="ButtonOpen">
<property name="toolTip">
<string>Opens an existing material card</string>
</property>
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ButtonSave">
<property name="toolTip">
<string>Saves this material as a card</string>
</property>
<property name="text">
<string>Save as...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="ButtonOpen">
<property name="toolTip">
<string>Opens an existing material card</string>
</property>
<property name="text">
<string>Open...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ButtonSave">
<property name="toolTip">
<string>Saves this material as a card</string>
</property>
<property name="text">
<string>Save as...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="PreviewGroup">
<property name="title">
<string>Preview</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="PreviewRender">
<property name="minimumSize">
<size>
<width>96</width>
<height>96</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="PreviewVector">
<property name="minimumSize">
<size>
<width>96</width>
<height>96</height>
</size>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@@ -99,11 +99,12 @@ def get_material_resources(category='Solid'):
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)
)
# fail silently
#else:
# FreeCAD.Console.PrintError(
# 'Custom material directory set by user: {} does not exist.\n'
# .format(custom_mat_dir)
# )
return resources
@@ -229,14 +230,18 @@ def get_material_template(withSpaces=False):
# on attributes, add a space before a capital letter
# will be used for better display in the ui
import re
new_template = []
for group in template_data:
new_group = {}
gg = list(group.keys())[0] # group dict has only one key
# iterating over a dict and changing it is not allowed
# thus it is iterated over a list of the keys
new_group[gg] = {}
for proper in list(group[gg].keys()):
new_proper = re.sub(r"(\w)([A-Z]+)", r"\1 \2", proper)
group[gg][new_proper] = group[gg][proper]
del group[gg][proper]
new_group[gg][new_proper] = group[gg][proper]
new_template.append(new_group)
template_data = new_template
return template_data