416 lines
16 KiB
Python
416 lines
16 KiB
Python
# -*- coding: utf-8 -*-
|
|
# ***************************************************************************
|
|
# * Copyright (c) 2020 sliptonic <shopinthewoods@gmail.com> *
|
|
# * *
|
|
# * 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.PathPropertyBag as PathPropertyBag
|
|
import PathScripts.PathPropertyEditor as PathPropertyEditor
|
|
import PathScripts.PathUtil as PathUtil
|
|
|
|
from PySide import QtCore, QtGui
|
|
|
|
__title__ = "Property Bag Editor"
|
|
__author__ = "sliptonic (Brad Collette)"
|
|
__url__ = "https://www.freecadweb.org"
|
|
__doc__ = "Task panel editor for a PropertyBag"
|
|
|
|
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
|
#PathLog.trackModule(PathLog.thisModule())
|
|
|
|
# Qt translation handling
|
|
def translate(context, text, disambig=None):
|
|
return QtCore.QCoreApplication.translate(context, text, disambig)
|
|
|
|
class ViewProvider(object):
|
|
'''ViewProvider for a PropertyBag.
|
|
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 PropertyCreate(object):
|
|
|
|
def __init__(self, obj, grp, typ, another):
|
|
self.obj = obj
|
|
self.form = FreeCADGui.PySideUic.loadUi(":panels/PropertyCreate.ui")
|
|
|
|
for g in sorted(obj.CustomPropertyGroups):
|
|
self.form.propertyGroup.addItem(g)
|
|
if grp:
|
|
self.form.propertyGroup.setCurrentText(grp)
|
|
|
|
for t in sorted(PathPropertyBag.SupportedPropertyType):
|
|
self.form.propertyType.addItem(t)
|
|
if PathPropertyBag.SupportedPropertyType[t] == typ:
|
|
typ = t
|
|
if typ:
|
|
self.form.propertyType.setCurrentText(typ)
|
|
else:
|
|
self.form.propertyType.setCurrentText('String')
|
|
self.form.createAnother.setChecked(another)
|
|
|
|
self.form.propertyGroup.currentTextChanged.connect(self.updateUI)
|
|
self.form.propertyGroup.currentIndexChanged.connect(self.updateUI)
|
|
self.form.propertyName.textChanged.connect(self.updateUI)
|
|
self.form.propertyType.currentIndexChanged.connect(self.updateUI)
|
|
self.form.enumValues.textChanged.connect(self.updateUI)
|
|
|
|
def updateUI(self):
|
|
typeSet = True
|
|
if self.propertyIsEnumeration():
|
|
self.form.enumLabel.setEnabled(True)
|
|
self.form.enumValues.setEnabled(True)
|
|
typeSet = self.form.enumValues.text().strip() != ''
|
|
else:
|
|
self.form.enumLabel.setEnabled(False)
|
|
self.form.enumValues.setEnabled(False)
|
|
if self.form.enumValues.text().strip():
|
|
self.form.enumValues.setText('')
|
|
|
|
ok = self.form.buttonBox.button(QtGui.QDialogButtonBox.Ok)
|
|
|
|
if typeSet and self.propertyName() and self.propertyGroup():
|
|
ok.setEnabled(True)
|
|
else:
|
|
ok.setEnabled(False)
|
|
|
|
def propertyName(self):
|
|
return self.form.propertyName.text().strip()
|
|
def propertyGroup(self):
|
|
return self.form.propertyGroup.currentText().strip()
|
|
def propertyType(self):
|
|
return PathPropertyBag.SupportedPropertyType[self.form.propertyType.currentText()].strip()
|
|
def propertyInfo(self):
|
|
return self.form.propertyInfo.toPlainText().strip()
|
|
def createAnother(self):
|
|
return self.form.createAnother.isChecked()
|
|
def propertyEnumerations(self):
|
|
return [s.strip() for s in self.form.enumValues.text().strip().split(',')]
|
|
def propertyIsEnumeration(self):
|
|
return self.propertyType() == 'App::PropertyEnumeration'
|
|
|
|
def exec_(self, name):
|
|
if name:
|
|
# property exists - this is an edit operation
|
|
self.form.propertyName.setText(name)
|
|
if self.propertyIsEnumeration():
|
|
self.form.enumValues.setText(','.join(self.obj.getEnumerationsOfProperty(name)))
|
|
self.form.propertyInfo.setText(self.obj.getDocumentationOfProperty(name))
|
|
|
|
self.form.propertyName.setEnabled(False)
|
|
self.form.propertyType.setEnabled(False)
|
|
self.form.createAnother.setEnabled(False)
|
|
|
|
else:
|
|
self.form.propertyName.setText('')
|
|
self.form.propertyInfo.setText('')
|
|
self.form.enumValues.setText('')
|
|
#self.form.propertyName.setFocus()
|
|
|
|
self.updateUI()
|
|
|
|
return self.form.exec_()
|
|
|
|
Panel = []
|
|
|
|
class TaskPanel(object):
|
|
ColumnName = 0
|
|
#ColumnType = 1
|
|
ColumnVal = 1
|
|
#TableHeaders = ['Property', 'Type', 'Value']
|
|
TableHeaders = ['Property', 'Value']
|
|
|
|
def __init__(self, vobj):
|
|
self.obj = vobj.Object
|
|
self.props = sorted(self.obj.Proxy.getCustomProperties())
|
|
self.form = FreeCADGui.PySideUic.loadUi(":panels/PropertyBag.ui")
|
|
|
|
# initialized later
|
|
self.model = None
|
|
self.delegate = None
|
|
FreeCAD.ActiveDocument.openTransaction(translate("PathPropertyBag", "Edit PropertyBag"))
|
|
Panel.append(self)
|
|
|
|
def updateData(self, topLeft, bottomRight):
|
|
pass
|
|
|
|
|
|
def _setupProperty(self, i, name):
|
|
typ = PathPropertyBag.getPropertyTypeName(self.obj.getTypeIdOfProperty(name))
|
|
val = PathUtil.getPropertyValueString(self.obj, name)
|
|
info = self.obj.getDocumentationOfProperty(name)
|
|
|
|
self.model.setData(self.model.index(i, self.ColumnName), name, QtCore.Qt.EditRole)
|
|
#self.model.setData(self.model.index(i, self.ColumnType), typ, 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), val, QtCore.Qt.DisplayRole)
|
|
|
|
self.model.setData(self.model.index(i, self.ColumnName), typ, QtCore.Qt.ToolTipRole)
|
|
#self.model.setData(self.model.index(i, self.ColumnType), info, QtCore.Qt.ToolTipRole)
|
|
self.model.setData(self.model.index(i, self.ColumnVal), info, QtCore.Qt.ToolTipRole)
|
|
|
|
self.model.item(i, self.ColumnName).setEditable(False)
|
|
#self.model.item(i, self.ColumnType).setEditable(False)
|
|
|
|
def setupUi(self):
|
|
PathLog.track()
|
|
|
|
self.delegate = Delegate(self.form)
|
|
self.model = QtGui.QStandardItemModel(len(self.props), len(self.TableHeaders), self.form)
|
|
self.model.setHorizontalHeaderLabels(self.TableHeaders)
|
|
|
|
for i,name in enumerate(self.props):
|
|
self._setupProperty(i, name)
|
|
|
|
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.form.modify.clicked.connect(self.propertyModify)
|
|
self.propertySelected([])
|
|
|
|
def accept(self):
|
|
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.modify.setEnabled(True)
|
|
self.form.remove.setEnabled(True)
|
|
else:
|
|
self.form.modify.setEnabled(False)
|
|
self.form.remove.setEnabled(False)
|
|
|
|
def addCustomProperty(self, obj, dialog):
|
|
name = dialog.propertyName()
|
|
typ = dialog.propertyType()
|
|
grp = dialog.propertyGroup()
|
|
info = dialog.propertyInfo()
|
|
self.obj.Proxy.addCustomProperty(typ, name, grp, info)
|
|
if dialog.propertyIsEnumeration():
|
|
setattr(self.obj, name, dialog.propertyEnumerations())
|
|
return (name, info)
|
|
|
|
def propertyAdd(self):
|
|
PathLog.track()
|
|
more = False
|
|
grp = None
|
|
typ = None
|
|
while True:
|
|
dialog = PropertyCreate(self.obj, grp, typ, more)
|
|
if dialog.exec_(None):
|
|
# if we block signals the view doesn't get updated, surprise, surprise
|
|
#self.model.blockSignals(True)
|
|
name, info = self.addCustomProperty(self.obj, dialog)
|
|
index = 0
|
|
for i in range(self.model.rowCount()):
|
|
index = i
|
|
if self.model.item(i, self.ColumnName).data(QtCore.Qt.EditRole) > dialog.propertyName():
|
|
break
|
|
self.model.insertRows(index, 1)
|
|
self._setupProperty(index, name)
|
|
self.form.table.selectionModel().setCurrentIndex(self.model.index(index, 0), QtCore.QItemSelectionModel.Rows)
|
|
#self.model.blockSignals(False)
|
|
more = dialog.createAnother()
|
|
else:
|
|
more = False
|
|
if not more:
|
|
break
|
|
|
|
def propertyModify(self):
|
|
PathLog.track()
|
|
rows = []
|
|
for index in self.form.table.selectionModel().selectedIndexes():
|
|
row = index.row()
|
|
if row in rows:
|
|
continue
|
|
rows.append(row)
|
|
|
|
obj = self.model.item(row, self.ColumnVal).data(Delegate.RoleObject)
|
|
nam = self.model.item(row, self.ColumnVal).data(Delegate.RoleProperty)
|
|
grp = obj.getGroupOfProperty(nam)
|
|
typ = obj.getTypeIdOfProperty(nam)
|
|
|
|
dialog = PropertyCreate(self.obj, grp, typ, False)
|
|
if dialog.exec_(nam):
|
|
val = getattr(obj, nam)
|
|
obj.removeProperty(nam)
|
|
name, info = self.addCustomProperty(self.obj, dialog)
|
|
try:
|
|
setattr(obj, nam, val)
|
|
except:
|
|
# this can happen if the old enumeration value doesn't exist anymore
|
|
pass
|
|
newVal = PathUtil.getPropertyValueString(obj, nam)
|
|
self.model.setData(self.model.index(row, self.ColumnVal), newVal, QtCore.Qt.DisplayRole)
|
|
|
|
#self.model.setData(self.model.index(row, self.ColumnType), info, QtCore.Qt.ToolTipRole)
|
|
self.model.setData(self.model.index(row, self.ColumnVal), info, QtCore.Qt.ToolTipRole)
|
|
|
|
|
|
def propertyRemove(self):
|
|
PathLog.track()
|
|
# first find all rows which need to be removed
|
|
rows = []
|
|
for index in self.form.table.selectionModel().selectedIndexes():
|
|
if not index.row() in rows:
|
|
rows.append(index.row())
|
|
|
|
# then remove them in reverse order so the indexes of the remaining rows
|
|
# to delete are still valid
|
|
for row in reversed(sorted(rows)):
|
|
self.obj.removeProperty(self.model.item(row).data(QtCore.Qt.EditRole))
|
|
self.model.removeRow(row)
|
|
|
|
|
|
def Create(name = 'PropertyBag'):
|
|
'''Create(name = 'PropertyBag') ... creates a new setup sheet'''
|
|
FreeCAD.ActiveDocument.openTransaction(translate("PathPropertyBag", "Create PropertyBag"))
|
|
pcont = PathPropertyBag.Create(name)
|
|
PathIconViewProvider.Attach(pcont.ViewObject, name)
|
|
return pcont
|
|
|
|
PathIconViewProvider.RegisterViewProvider('PropertyBag', ViewProvider)
|
|
|
|
class PropertyBagCreateCommand(object):
|
|
'''Command to create a property container object'''
|
|
|
|
def __init__(self):
|
|
pass
|
|
|
|
def GetResources(self):
|
|
return {'MenuText': translate('PathPropertyBag', 'Property Bag'),
|
|
'ToolTip': translate('PathPropertyBag', 'Creates an object which can be used to store reference properties.')}
|
|
|
|
def IsActive(self):
|
|
return not FreeCAD.ActiveDocument is None
|
|
|
|
def Activated(self):
|
|
sel = FreeCADGui.Selection.getSelectionEx()
|
|
obj = Create()
|
|
body = None
|
|
if sel:
|
|
if 'PartDesign::Body' == sel[0].Object.TypeId:
|
|
body = sel[0].Object
|
|
elif hasattr(sel[0].Object, 'getParentGeoFeatureGroup'):
|
|
body = sel[0].Object.getParentGeoFeatureGroup()
|
|
if body:
|
|
obj.Label = 'Attributes'
|
|
group = body.Group
|
|
group.append(obj)
|
|
body.Group = group
|
|
|
|
if FreeCAD.GuiUp:
|
|
FreeCADGui.addCommand('Path_PropertyBag', PropertyBagCreateCommand())
|
|
|
|
FreeCAD.Console.PrintLog("Loading PathPropertyBagGui ... done\n")
|