diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt
index 444be1ecd4..28bc67fa96 100644
--- a/src/Mod/Path/CMakeLists.txt
+++ b/src/Mod/Path/CMakeLists.txt
@@ -96,6 +96,10 @@ SET(PathScripts_SRCS
PathScripts/PathProfileFaces.py
PathScripts/PathProfileFacesGui.py
PathScripts/PathProfileGui.py
+ PathScripts/PathProperty.py
+ PathScripts/PathPropertyContainer.py
+ PathScripts/PathPropertyContainerGui.py
+ PathScripts/PathPropertyEditor.py
PathScripts/PathSanity.py
PathScripts/PathSelection.py
PathScripts/PathSetupSheet.py
diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc
index c99246c667..a8d0ab5d21 100644
--- a/src/Mod/Path/Gui/Resources/Path.qrc
+++ b/src/Mod/Path/Gui/Resources/Path.qrc
@@ -121,6 +121,7 @@
panels/PageOpVcarveEdit.ui
panels/PathEdit.ui
panels/PointEdit.ui
+ panels/PropertyContainer.ui
panels/SetupGlobal.ui
panels/SetupOp.ui
panels/ToolBitEditor.ui
diff --git a/src/Mod/Path/Gui/Resources/panels/PropertyContainer.ui b/src/Mod/Path/Gui/Resources/panels/PropertyContainer.ui
new file mode 100644
index 0000000000..21df9f9cb5
--- /dev/null
+++ b/src/Mod/Path/Gui/Resources/panels/PropertyContainer.ui
@@ -0,0 +1,57 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 552
+ 651
+
+
+
+ Form
+
+
+ -
+
+
+ QAbstractItemView::AllEditTriggers
+
+
+ true
+
+
+ true
+
+
+ false
+
+
+
+ -
+
+
+
-
+
+
+ Remove
+
+
+
+ -
+
+
+ Add
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Mod/Path/PathScripts/PathGuiInit.py b/src/Mod/Path/PathScripts/PathGuiInit.py
index cd48d41309..29f285d59a 100644
--- a/src/Mod/Path/PathScripts/PathGuiInit.py
+++ b/src/Mod/Path/PathScripts/PathGuiInit.py
@@ -66,6 +66,7 @@ def Startup():
# from PathScripts import PathProfileEdgesGui
# from PathScripts import PathProfileFacesGui
from PathScripts import PathProfileGui
+ from PathScripts import PathPropertyContainerGui
from PathScripts import PathSanity
from PathScripts import PathSetupSheetGui
from PathScripts import PathSimpleCopy
diff --git a/src/Mod/Path/PathScripts/PathProperty.py b/src/Mod/Path/PathScripts/PathProperty.py
new file mode 100644
index 0000000000..7e3eeb1e6f
--- /dev/null
+++ b/src/Mod/Path/PathScripts/PathProperty.py
@@ -0,0 +1,201 @@
+# -*- coding: utf-8 -*-
+# ***************************************************************************
+# * Copyright (c) 2020 sliptonic *
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU Lesser General Public License (LGPL) *
+# * as published by the Free Software Foundation; either version 2 of *
+# * the License, or (at your option) any later version. *
+# * for detail see the LICENCE text file. *
+# * *
+# * This program is distributed in the hope that it will be useful, *
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+# * GNU Library General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Library General Public *
+# * License along with this program; if not, write to the Free Software *
+# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+# * USA *
+# * *
+# ***************************************************************************
+
+import PathScripts.PathLog as PathLog
+
+__title__ = "Property type abstraction for editing purposes"
+__author__ = "sliptonic (Brad Collette)"
+__url__ = "https://www.freecadweb.org"
+__doc__ = "Prototype objects to allow extraction of setup sheet values and editing."
+
+
+PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
+#PathLog.trackModule(PathLog.thisModule())
+
+class Property(object):
+ '''Base class for all prototype properties'''
+ def __init__(self, name, propType, category, info):
+ self.name = name
+ self.propType = propType
+ self.category = category
+ self.info = info
+ self.editorMode = 0
+ self.value = None
+
+ def setValue(self, value):
+ self.value = value
+ def getValue(self):
+ return self.value
+
+ def setEditorMode(self, mode):
+ self.editorMode = mode
+
+ def displayString(self):
+ if self.value is None:
+ t = self.typeString()
+ p = 'an' if t[0] in ['A', 'E', 'I', 'O', 'U'] else 'a'
+ return "%s %s" % (p, t)
+ return self.value
+
+ def typeString(self):
+ return "Property"
+
+ def setupProperty(self, obj, name, category, value):
+ created = False
+ if not hasattr(obj, name):
+ obj.addProperty(self.propType, name, category, self.info)
+ self.initProperty(obj, name)
+ created = True
+ setattr(obj, name, value)
+ return created
+
+ def initProperty(self, obj, name):
+ pass
+
+ def setValueFromString(self, string):
+ self.setValue(self.valueFromString(string))
+
+ def valueFromString(self, string):
+ return string
+
+class PropertyEnumeration(Property):
+ def typeString(self):
+ return "Enumeration"
+
+ def setValue(self, value):
+ if list == type(value):
+ self.enums = value # pylint: disable=attribute-defined-outside-init
+ else:
+ super(PropertyEnumeration, self).setValue(value)
+
+ def getEnumValues(self):
+ return self.enums
+
+ def initProperty(self, obj, name):
+ setattr(obj, name, self.enums)
+
+class PropertyQuantity(Property):
+ def displayString(self):
+ if self.value is None:
+ return Property.displayString(self)
+ return self.value.getUserPreferred()[0]
+
+class PropertyAngle(PropertyQuantity):
+ def typeString(self):
+ return "Angle"
+
+class PropertyDistance(PropertyQuantity):
+ def typeString(self):
+ return "Distance"
+
+class PropertyLength(PropertyQuantity):
+ def typeString(self):
+ return "Length"
+
+class PropertyPercent(Property):
+ def typeString(self):
+ return "Percent"
+
+class PropertyFloat(Property):
+ def typeString(self):
+ return "Float"
+
+ def valueFromString(self, string):
+ return float(string)
+
+class PropertyInteger(Property):
+ def typeString(self):
+ return "Integer"
+
+ def valueFromString(self, string):
+ return int(string)
+
+class PropertyBool(Property):
+ def typeString(self):
+ return "Bool"
+
+ def valueFromString(self, string):
+ return bool(string)
+
+class PropertyString(Property):
+ def typeString(self):
+ return "String"
+
+class PropertyMap(Property):
+ def typeString(self):
+ return "Map"
+
+ def displayString(self, value):
+ return str(value)
+
+class OpPrototype(object):
+
+ PropertyType = {
+ 'App::PropertyAngle': PropertyAngle,
+ 'App::PropertyBool': PropertyBool,
+ 'App::PropertyDistance': PropertyDistance,
+ 'App::PropertyEnumeration': PropertyEnumeration,
+ 'App::PropertyFile': PropertyString,
+ 'App::PropertyFloat': PropertyFloat,
+ 'App::PropertyFloatConstraint': Property,
+ 'App::PropertyFloatList': Property,
+ 'App::PropertyInteger': PropertyInteger,
+ 'App::PropertyIntegerList': PropertyInteger,
+ 'App::PropertyLength': PropertyLength,
+ 'App::PropertyLink': Property,
+ 'App::PropertyLinkList': Property,
+ 'App::PropertyLinkSubListGlobal': Property,
+ 'App::PropertyMap': PropertyMap,
+ 'App::PropertyPercent': PropertyPercent,
+ 'App::PropertyString': PropertyString,
+ 'App::PropertyStringList': Property,
+ 'App::PropertyVectorDistance': Property,
+ 'App::PropertyVectorList': Property,
+ 'Part::PropertyPartShape': Property,
+ }
+
+ def __init__(self, name):
+ self.Label = name
+ self.properties = {}
+ self.DoNotSetDefaultValues = True
+ self.Proxy = None
+
+ def __setattr__(self, name, val):
+ if name in ['Label', 'DoNotSetDefaultValues', 'properties', 'Proxy']:
+ if name == 'Proxy':
+ val = None # make sure the proxy is never set
+ return super(OpPrototype, self).__setattr__(name, val)
+ self.properties[name].setValue(val)
+
+ def addProperty(self, typeString, name, category, info = None):
+ prop = self.PropertyType[typeString](name, typeString, category, info)
+ self.properties[name] = prop
+ return self
+
+ def setEditorMode(self, name, mode):
+ self.properties[name].setEditorMode(mode)
+
+ def getProperty(self, name):
+ return self.properties[name]
+
+ def setupProperties(self, setup):
+ return [p for p in self.properties if p.name in setup]
diff --git a/src/Mod/Path/PathScripts/PathPropertyContainer.py b/src/Mod/Path/PathScripts/PathPropertyContainer.py
new file mode 100644
index 0000000000..a1b52b8e08
--- /dev/null
+++ b/src/Mod/Path/PathScripts/PathPropertyContainer.py
@@ -0,0 +1,89 @@
+# -*- coding: utf-8 -*-
+# ***************************************************************************
+# * Copyright (c) 2020 sliptonic *
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU Lesser General Public License (LGPL) *
+# * as published by the Free Software Foundation; either version 2 of *
+# * the License, or (at your option) any later version. *
+# * for detail see the LICENCE text file. *
+# * *
+# * This program is distributed in the hope that it will be useful, *
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+# * GNU Library General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Library General Public *
+# * License along with this program; if not, write to the Free Software *
+# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+# * USA *
+# * *
+# ***************************************************************************
+
+import FreeCAD
+import PySide
+
+__title__ = 'Generic property container to store some values.'
+__author__ = 'sliptonic (Brad Collette)'
+__url__ = 'https://www.freecadweb.org'
+__doc__ = 'A generic container for typed properties in arbitrary categories.'
+
+def translate(context, text, disambig=None):
+ return PySide.QtCore.QCoreApplication.translate(context, text, disambig)
+
+
+class PropertyContainer(object):
+ '''Property container object.'''
+
+ CustomPropertyGroups = 'CustomPropertyGroups'
+ CustomPropertyGroupDefault = 'User'
+
+ def __init__(self, obj):
+ self.obj = obj
+ obj.addProperty('App::PropertyStringList', self.CustomPropertyGroups, 'Base', PySide.QtCore.QT_TRANSLATE_NOOP('PathPropertyContainer', 'List of custom property groups'))
+ obj.setEditorMode(self.CustomPropertyGroups, 2) # hide
+
+ def __getstate__(self):
+ return None
+
+ def __setstate__(self, state):
+ for obj in FreeCAD.ActiveDocument.Objects:
+ if hasattr(obj, 'Proxy') and obj.Proxy == self:
+ self.obj = obj
+ obj.setEditorMode(self.CustomPropertyGroups, 2) # hide
+ break
+ return None
+
+ def onDocumentRestored(self, obj):
+ obj.setEditorMode(self.CustomPropertyGroups, 2) # hide
+
+ def getCustomProperties(self):
+ '''Return a list of all custom properties created in this container.'''
+ return [p for p in self.obj.PropertiesList if self.obj.getGroupOfProperty(p) in self.obj.CustomPropertyGroups]
+
+ def addCustomProperty(self, propertyType, name, group=None, desc=None):
+ '''addCustomProperty(propertyType, name, group=None, desc=None) ... adds a custom property and tracks its group.'''
+ if desc is None:
+ desc = ''
+ if group is None:
+ group = self.CustomPropertyGroupDefault
+ groups = self.obj.CustomPropertyGroups
+ if not group in groups:
+ groups.append(group)
+ self.obj.CustomPropertyGroups = groups
+ self.obj.addProperty(propertyType, name, group, desc)
+
+def Create(name = 'PropertyContainer'):
+ obj = FreeCAD.ActiveDocument.addObject('App::FeaturePython', name)
+ obj.Proxy = PropertyContainer(obj)
+ return obj
+
+def IsPropertyContainer(obj):
+ '''Returns True if the supplied object is a property container (or its Proxy).'''
+
+ if type(obj) == PropertyContainer:
+ return True
+ if hasattr(obj, 'Proxy'):
+ return IsPropertyContainer(obj.Proxy)
+ return False
+
diff --git a/src/Mod/Path/PathScripts/PathPropertyContainerGui.py b/src/Mod/Path/PathScripts/PathPropertyContainerGui.py
new file mode 100644
index 0000000000..53b79fa517
--- /dev/null
+++ b/src/Mod/Path/PathScripts/PathPropertyContainerGui.py
@@ -0,0 +1,235 @@
+# -*- coding: utf-8 -*-
+# ***************************************************************************
+# * Copyright (c) 2020 sliptonic *
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU Lesser General Public License (LGPL) *
+# * as published by the Free Software Foundation; either version 2 of *
+# * the License, or (at your option) any later version. *
+# * for detail see the LICENCE text file. *
+# * *
+# * This program is distributed in the hope that it will be useful, *
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+# * GNU Library General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Library General Public *
+# * License along with this program; if not, write to the Free Software *
+# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+# * USA *
+# * *
+# ***************************************************************************
+
+import FreeCAD
+import FreeCADGui
+import PathScripts.PathGui as PathGui
+import PathScripts.PathIconViewProvider as PathIconViewProvider
+import PathScripts.PathLog as PathLog
+import PathScripts.PathPropertyContainer as PathPropertyContainer
+import PathScripts.PathPropertyEditor as PathPropertyEditor
+import PathScripts.PathUtil as PathUtil
+
+from PySide import QtCore, QtGui
+
+__title__ = "Property Container Editor"
+__author__ = "sliptonic (Brad Collette)"
+__url__ = "https://www.freecadweb.org"
+__doc__ = "Task panel editor for a PropertyContainer"
+
+PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
+PathLog.trackModule(PathLog.thisModule())
+
+# Qt translation handling
+def translate(context, text, disambig=None):
+ return QtCore.QCoreApplication.translate(context, text, disambig)
+
+
+class ViewProvider:
+ '''ViewProvider for a PropertyContainer.
+ It's sole job is to provide an icon and invoke the TaskPanel on edit.'''
+
+ def __init__(self, vobj, name):
+ PathLog.track(name)
+ vobj.Proxy = self
+ self.icon = name
+ # mode = 2
+ self.obj = None
+ self.vobj = None
+
+ def attach(self, vobj):
+ PathLog.track()
+ self.vobj = vobj
+ self.obj = vobj.Object
+
+ def getIcon(self):
+ return ":/icons/Path-SetupSheet.svg"
+
+ def __getstate__(self):
+ return None
+
+ def __setstate__(self, state):
+ # pylint: disable=unused-argument
+ return None
+
+ def getDisplayMode(self, mode):
+ # pylint: disable=unused-argument
+ return 'Default'
+
+ def setEdit(self, vobj, mode=0):
+ # pylint: disable=unused-argument
+ PathLog.track()
+ taskPanel = TaskPanel(vobj)
+ FreeCADGui.Control.closeDialog()
+ FreeCADGui.Control.showDialog(taskPanel)
+ taskPanel.setupUi()
+ return True
+
+ def unsetEdit(self, vobj, mode):
+ # pylint: disable=unused-argument
+ FreeCADGui.Control.closeDialog()
+ return
+
+ def claimChildren(self):
+ return []
+
+ def doubleClicked(self, vobj):
+ self.setEdit(vobj)
+
+class Delegate(QtGui.QStyledItemDelegate):
+ RoleObject = QtCore.Qt.UserRole + 1
+ RoleProperty = QtCore.Qt.UserRole + 2
+ RoleEditor = QtCore.Qt.UserRole + 3
+
+
+ #def paint(self, painter, option, index):
+ # #PathLog.track(index.column(), type(option))
+
+ def createEditor(self, parent, option, index):
+ # pylint: disable=unused-argument
+ editor = PathPropertyEditor.Editor(index.data(self.RoleObject), index.data(self.RoleProperty))
+ index.model().setData(index, editor, self.RoleEditor)
+ return editor.widget(parent)
+
+ def setEditorData(self, widget, index):
+ PathLog.track(index.row(), index.column())
+ index.data(self.RoleEditor).setEditorData(widget)
+
+ def setModelData(self, widget, model, index):
+ # pylint: disable=unused-argument
+ PathLog.track(index.row(), index.column())
+ editor = index.data(self.RoleEditor)
+ editor.setModelData(widget)
+ index.model().setData(index, editor.displayString(), QtCore.Qt.DisplayRole)
+
+ def updateEditorGeometry(self, widget, option, index):
+ # pylint: disable=unused-argument
+ widget.setGeometry(option.rect)
+
+class TaskPanel:
+ ColumnName = 0
+ ColumnVal = 1
+ ColumnDesc = 2
+
+ def __init__(self, vobj):
+ self.obj = vobj.Object
+ self.props = sorted(self.obj.Proxy.getCustomProperties())
+ self.form = FreeCADGui.PySideUic.loadUi(":panels/PropertyContainer.ui")
+
+ # initialized later
+ self.delegate = None
+ self.model = None
+ FreeCAD.ActiveDocument.openTransaction(translate("PathPropertyContainer", "Edit PropertyContainer"))
+
+ def updateData(self, topLeft, bottomRight):
+ # pylint: disable=unused-argument
+ #if 0 == topLeft.column():
+ # isset = self.model.item(topLeft.row(), 0).checkState() == QtCore.Qt.Checked
+ # self.model.item(topLeft.row(), 1).setEnabled(isset)
+ # self.model.item(topLeft.row(), 2).setEnabled(isset)
+ print("index = ({}, {}) - ({}, {})".format(topLeft.row(), topLeft.column(), bottomRight.row(), bottomRight.column()))
+ if topLeft.column() == self.ColumnDesc:
+ obj = topLeft.data(Delegate.RoleObject)
+ prop = topLeft.data(Delegate.RoleProperty)
+
+ def setupUi(self):
+ PathLog.track()
+
+ self.delegate = Delegate(self.form)
+ self.model = QtGui.QStandardItemModel(len(self.props), 3, self.form)
+ self.model.setHorizontalHeaderLabels(['Property', 'Value', 'Description'])
+
+ for i,name in enumerate(self.props):
+ info = self.obj.getDocumentationOfProperty(name)
+ value = self.obj.getPropertyByName(name)
+ self.model.setData(self.model.index(i, self.ColumnName), name, QtCore.Qt.EditRole)
+ self.model.setData(self.model.index(i, self.ColumnVal), self.obj, Delegate.RoleObject)
+ self.model.setData(self.model.index(i, self.ColumnVal), name, Delegate.RoleProperty)
+ self.model.setData(self.model.index(i, self.ColumnVal), str(value), QtCore.Qt.DisplayRole)
+ self.model.setData(self.model.index(i, self.ColumnDesc), info, QtCore.Qt.EditRole)
+
+ self.model.item(i, self.ColumnName).setEditable(False)
+
+ self.form.table.setModel(self.model)
+ self.form.table.setItemDelegateForColumn(self.ColumnVal, self.delegate)
+ self.form.table.resizeColumnsToContents()
+
+ self.model.dataChanged.connect(self.updateData)
+ self.form.table.selectionModel().selectionChanged.connect(self.propertySelected)
+ self.form.add.clicked.connect(self.propertyAdd)
+ self.form.remove.clicked.connect(self.propertyRemove)
+ self.propertySelected([])
+
+ def accept(self):
+ #propertiesCreatedRemoved = False
+ #for i,name in enumerate(self.props):
+ # prop = self.prototype.getProperty(name)
+ # propName = self.propertyName(name)
+ # enabled = self.model.item(i, 0).checkState() == QtCore.Qt.Checked
+ # if enabled and not prop.getValue() is None:
+ # if prop.setupProperty(self.obj, propName, self.propertyGroup(), prop.getValue()):
+ # propertiesCreatedRemoved = True
+ # else:
+ # if hasattr(self.obj, propName):
+ # self.obj.removeProperty(propName)
+ # propertiesCreatedRemoved = True
+
+ FreeCAD.ActiveDocument.commitTransaction()
+ FreeCADGui.ActiveDocument.resetEdit()
+ FreeCADGui.Control.closeDialog()
+ FreeCAD.ActiveDocument.recompute()
+
+ def reject(self):
+ FreeCAD.ActiveDocument.abortTransaction()
+ FreeCADGui.Control.closeDialog()
+ FreeCAD.ActiveDocument.recompute()
+
+ def propertySelected(self, selection):
+ PathLog.track()
+ if selection:
+ self.form.remove.setEnabled(True)
+ else:
+ self.form.remove.setEnabled(False)
+
+ def propertyAdd(self):
+ PathLog.track()
+
+ def propertyRemove(self):
+ PathLog.track()
+ rows = []
+ for index in self.form.table.selectionModel().selectedIndexes():
+ if not index.row() in rows:
+ rows.append(index.row())
+ for row in reversed(sorted(rows)):
+ self.obj.removeProperty(self.model.item(row).data(QtCore.Qt.EditRole))
+ self.model.removeRow(row)
+
+
+def Create(name = 'PropertyContainer'):
+ '''Create(name = 'PropertyContainer') ... creates a new setup sheet'''
+ FreeCAD.ActiveDocument.openTransaction(translate("PathPropertyContainer", "Create PropertyContainer"))
+ pcont = PathPropertyContainer.Create(name)
+ PathIconViewProvider.Attach(pcont.ViewObject, name)
+ return pcont
+
+PathIconViewProvider.RegisterViewProvider('PropertyContainer', ViewProvider)
+
diff --git a/src/Mod/Path/PathScripts/PathPropertyEditor.py b/src/Mod/Path/PathScripts/PathPropertyEditor.py
new file mode 100644
index 0000000000..9b0cd81afd
--- /dev/null
+++ b/src/Mod/Path/PathScripts/PathPropertyEditor.py
@@ -0,0 +1,219 @@
+# -*- coding: utf-8 -*-
+# ***************************************************************************
+# * Copyright (c) 2020 sliptonic *
+# * *
+# * This program is free software; you can redistribute it and/or modify *
+# * it under the terms of the GNU Lesser General Public License (LGPL) *
+# * as published by the Free Software Foundation; either version 2 of *
+# * the License, or (at your option) any later version. *
+# * for detail see the LICENCE text file. *
+# * *
+# * This program is distributed in the hope that it will be useful, *
+# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+# * GNU Library General Public License for more details. *
+# * *
+# * You should have received a copy of the GNU Library General Public *
+# * License along with this program; if not, write to the Free Software *
+# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
+# * USA *
+# * *
+# ***************************************************************************
+
+import FreeCAD
+import PathScripts.PathLog as PathLog
+import PathScripts.PathSetupSheetOpPrototype as PathSetupSheetOpPrototype
+
+from PySide import QtCore, QtGui
+
+__title__ = "Path Property Editor"
+__author__ = "sliptonic (Brad Collette)"
+__url__ = "https://www.freecadweb.org"
+__doc__ = "Task panel editor for Properties"
+
+# Qt translation handling
+def translate(context, text, disambig=None):
+ return QtCore.QCoreApplication.translate(context, text, disambig)
+
+LOGLEVEL = False
+
+if LOGLEVEL:
+ PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
+ PathLog.trackModule(PathLog.thisModule())
+else:
+ PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
+
+class _PropertyEditor(object):
+ '''Base class of all property editors - just outlines the TableView delegate interface.'''
+ def __init__(self, obj, prop):
+ self.obj = obj
+ self.prop = prop
+
+ def widget(self, parent):
+ '''widget(parent) ... called by the delegate to get a new editor widget.
+ Must be implemented by subclasses and return the widget.'''
+ pass # pylint: disable=unnecessary-pass
+
+ def setEditorData(self, widget):
+ '''setEditorData(widget) ... called by the delegate to initialize the editor.
+ The widget is the object returned by widget().
+ Must be implemented by subclasses.'''
+ pass # pylint: disable=unnecessary-pass
+
+ def setModelData(self, widget):
+ '''setModelData(widget) ... called by the delegate to store new values.
+ Must be implemented by subclasses.'''
+ pass # pylint: disable=unnecessary-pass
+
+ def propertyValue(self):
+ return self.obj.getPropertyByName(self.prop)
+
+ def setProperty(self, value):
+ setattr(self.obj, self.prop, value)
+
+ def displayString(self):
+ return self.propertyValue()
+
+class _PropertyEditorBool(_PropertyEditor):
+ '''Editor for boolean values - uses a combo box.'''
+
+ def widget(self, parent):
+ return QtGui.QComboBox(parent)
+
+ def setEditorData(self, widget):
+ widget.clear()
+ widget.addItems(['false', 'true'])
+ index = 1 if self.propertyValue() else 0
+ widget.setCurrentIndex(index)
+
+ def setModelData(self, widget):
+ self.setProperty(widget.currentText() == 'true')
+
+class _PropertyEditorString(_PropertyEditor):
+ '''Editor for string values - uses a line edit.'''
+
+ def widget(self, parent):
+ return QtGui.QLineEdit(parent)
+
+ def setEditorData(self, widget):
+ text = '' if self.propertyValue() is None else self.propertyValue()
+ widget.setText(text)
+
+ def setModelData(self, widget):
+ self.setProperty(widget.text())
+
+class _PropertyEditorQuantity(_PropertyEditor):
+
+ def widget(self, parent):
+ return QtGui.QLineEdit(parent)
+
+ def setEditorData(self, widget):
+ quantity = self.propertyValue()
+ if quantity is None:
+ quantity = self.defaultQuantity()
+ widget.setText(quantity.getUserPreferred()[0])
+
+ def defaultQuantity(self):
+ pass
+
+ def setModelData(self, widget):
+ self.setProperty(FreeCAD.Units.Quantity(widget.text()))
+
+ def displayString(self):
+ if self.propertyValue() is None:
+ return ''
+ return self.propertyValue().getUserPreferred()[0]
+
+class _PropertyEditorAngle(_PropertyEditorQuantity):
+ '''Editor for angle values - uses a line edit'''
+
+ def defaultQuantity(self):
+ return FreeCAD.Units.Quantity(0, FreeCAD.Units.Angle)
+
+class _PropertyEditorLength(_PropertyEditorQuantity):
+ '''Editor for length values - uses a line edit.'''
+
+ def defaultQuantity(self):
+ return FreeCAD.Units.Quantity(0, FreeCAD.Units.Length)
+
+class _PropertyEditorPercent(_PropertyEditor):
+ '''Editor for percent values - uses a spin box.'''
+
+ def widget(self, parent):
+ return QtGui.QSpinBox(parent)
+
+ def setEditorData(self, widget):
+ widget.setRange(0, 100)
+ value = self.propertyValue()
+ if value is None:
+ value = 0
+ widget.setValue(value)
+
+ def setModelData(self, widget):
+ self.setProperty(widget.value())
+
+class _PropertyEditorInteger(_PropertyEditor):
+ '''Editor for integer values - uses a spin box.'''
+
+ def widget(self, parent):
+ return QtGui.QSpinBox(parent)
+
+ def setEditorData(self, widget):
+ value = self.propertyValue()
+ if value is None:
+ value = 0
+ widget.setValue(value)
+
+ def setModelData(self, widget):
+ self.setProperty(widget.value())
+
+class _PropertyEditorFloat(_PropertyEditor):
+ '''Editor for float values - uses a double spin box.'''
+
+ def widget(self, parent):
+ return QtGui.QDoubleSpinBox(parent)
+
+ def setEditorData(self, widget):
+ value = self.propertyValue()
+ if value is None:
+ value = 0.0
+ widget.setValue(value)
+
+ def setModelData(self, widget):
+ self.setProperty(widget.value())
+
+class _PropertyEditorFile(_PropertyEditor):
+
+ def widget(self, parent):
+ return QtGui.QLineEdit(parent)
+
+ def setEditorData(self, widget):
+ text = '' if self.propertyValue() is None else self.propertyValue()
+ widget.setText(text)
+
+ def setModelData(self, widget):
+ self.setProperty(widget.text())
+
+_EditorFactory = {
+ 'App::PropertyAngle' : _PropertyEditorAngle,
+ 'App::PropertyBool' : _PropertyEditorBool,
+ 'App::PropertyDistance' : _PropertyEditorLength,
+ #'App::PropertyEnumeration' : _PropertyEditorEnum,
+ #'App::PropertyFile' : _PropertyEditorFile,
+ 'App::PropertyFloat' : _PropertyEditorFloat,
+ 'App::PropertyInteger' : _PropertyEditorInteger,
+ 'App::PropertyLength' : _PropertyEditorLength,
+ 'App::PropertyPercent' : _PropertyEditorPercent,
+ 'App::PropertyString' : _PropertyEditorString,
+ }
+
+def Types():
+ '''Return the types of properties supported.'''
+ return [t for t in _EditorFactory]
+
+def Editor(obj, prop):
+ '''Returns an editor class to be used for the given property.'''
+ factory = _EditorFactory[obj.getTypeIdOfProperty(prop)]
+ if factory:
+ return factory(obj, prop)
+ return None