| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | 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 |
| | |
| | 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 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: |
| | |
| | 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.updateUI() |
| |
|
| | return self.form.exec_() |
| |
|
| |
|
| | class TaskPanel(object): |
| | ColumnName = 0 |
| | |
| | ColumnVal = 1 |
| | |
| | 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") |
| |
|
| | |
| | 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.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.ColumnVal), info, QtCore.Qt.ToolTipRole) |
| |
|
| | self.model.item(i, self.ColumnName).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): |
| | |
| | |
| | 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 |
| | ) |
| | |
| | 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: |
| | |
| | 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.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() |
| | |
| | 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="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") |
| |
|