FreeCAD / src /Mod /CAM /Path /Base /Gui /PropertyBag.py
AbdulElahGwaith's picture
Upload folder using huggingface_hub
985c397 verified
# SPDX-License-Identifier: LGPL-2.1-or-later
# ***************************************************************************
# * 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 *
# * *
# ***************************************************************************
from PySide import QtCore, QtGui
import FreeCAD
import FreeCADGui
import Path
import Path.Base.Gui.IconViewProvider as PathIconViewProvider
import Path.Base.Gui.PropertyEditor as PathPropertyEditor
import Path.Base.PropertyBag as PathPropertyBag
import Path.Base.Util as PathUtil
import re
__title__ = "Property Bag Editor"
__author__ = "sliptonic (Brad Collette)"
__url__ = "https://www.freecad.org"
__doc__ = "Task panel editor for a PropertyBag"
if False:
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
Path.Log.trackModule(Path.Log.thisModule())
else:
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
translate = FreeCAD.Qt.translate
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):
Path.Log.track(name)
vobj.Proxy = self
self.icon = name
# mode = 2
self.obj = None
self.vobj = None
def attach(self, vobj):
Path.Log.track()
self.vobj = vobj
self.obj = vobj.Object
def getIcon(self):
return ":/icons/Path-SetupSheet.svg"
def dumps(self):
return None
def loads(self, state):
return None
def getDisplayMode(self, mode):
return "Default"
def setEdit(self, vobj, mode=0):
Path.Log.track()
taskPanel = TaskPanel(vobj)
FreeCADGui.Control.closeDialog()
FreeCADGui.Control.showDialog(taskPanel)
taskPanel.setupUi()
return True
def unsetEdit(self, vobj, mode):
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):
# #Path.Log.track(index.column(), type(option))
def createEditor(self, parent, option, index):
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):
Path.Log.track(index.row(), index.column())
index.data(self.RoleEditor).setEditorData(widget)
def setModelData(self, widget, model, index):
Path.Log.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):
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")
obj.Proxy.refreshCustomPropertyGroups()
groups = obj.CustomPropertyGroups
if not isinstance(groups, (list, tuple)):
groups = [groups]
for g in sorted(groups):
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.propertyEnum.textChanged.connect(self.updateUI)
def updateUI(self):
typeSet = True
if self.propertyIsEnumeration():
self.form.labelEnum.setEnabled(True)
self.form.propertyEnum.setEnabled(True)
typeSet = self.form.propertyEnum.text().strip() != ""
else:
self.form.labelEnum.setEnabled(False)
self.form.propertyEnum.setEnabled(False)
if self.form.propertyEnum.text().strip():
self.form.propertyEnum.setText("")
ok = self.form.buttonBox.button(QtGui.QDialogButtonBox.Ok)
if not re.match("^[A-Za-z0-9_]*$", self.form.propertyName.text()):
typeSet = False
if typeSet 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.propertyEnum.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.propertyEnum.setText(",".join(self.obj.getEnumerationsOfProperty(name)))
self.form.propertyInfo.setText(self.obj.getDocumentationOfProperty(name))
self.form.labelName.setEnabled(False)
self.form.propertyName.setEnabled(False)
self.form.labelType.setEnabled(False)
self.form.propertyType.setEnabled(False)
self.form.createAnother.setEnabled(False)
else:
self.form.propertyName.setText("")
self.form.propertyInfo.setText("")
self.form.propertyEnum.setText("")
# self.form.propertyName.setFocus()
self.updateUI()
return self.form.exec_()
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("Edit PropertyBag")
def updateData(self, topLeft, bottomRight):
pass
def _setupProperty(self, i, name):
if name not in self.obj.PropertiesList:
Path.Log.warning(f"Property '{name}' not found in object {self.obj.Name}")
return
prop_type_id = self.obj.getTypeIdOfProperty(name)
try:
typ = PathPropertyBag.getPropertyTypeName(prop_type_id)
except IndexError:
Path.Log.error(
f"Unknown property type id '{prop_type_id}' for property '{name}' in object {self.obj.Name}"
)
return
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):
Path.Log.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.form.table.doubleClicked.connect(self.propertyModifyIndex)
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):
Path.Log.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()
if name:
propname = self.obj.Proxy.addCustomProperty(typ, name, grp, info)
if dialog.propertyIsEnumeration():
setattr(self.obj, name, dialog.propertyEnumerations())
return (propname, info)
return (None, None)
def propertyAdd(self):
Path.Log.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)
if name:
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
else:
more = False
if not more:
break
def propertyModifyIndex(self, index):
Path.Log.track(index.row(), index.column())
row = index.row()
obj = self.model.item(row, self.ColumnVal).data(Delegate.RoleObject)
prop = self.model.item(row, self.ColumnVal).data(Delegate.RoleProperty)
grp = obj.getGroupOfProperty(prop)
typ = obj.getTypeIdOfProperty(prop)
dialog = PropertyCreate(self.obj, grp, typ, False)
if dialog.exec_(prop):
val = getattr(obj, prop)
obj.removeProperty(prop)
name, info = self.addCustomProperty(self.obj, dialog)
try:
setattr(obj, prop, val)
except Exception:
# this can happen if the old enumeration value doesn't exist anymore
pass
newVal = PathUtil.getPropertyValueString(obj, prop)
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 propertyModify(self):
Path.Log.track()
rows = []
for index in self.form.table.selectionModel().selectedIndexes():
row = index.row()
if row in rows:
continue
rows.append(row)
self.propertyModifyIndex(index)
def propertyRemove(self):
Path.Log.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("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("CAM_PropertyBag", "Property Bag"),
"ToolTip": translate(
"CAM_PropertyBag",
"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("CAM_PropertyBag", PropertyBagCreateCommand())
FreeCAD.Console.PrintLog("Loading PathPropertyBagGui… done\n")