diff --git a/src/Mod/Path/Gui/Resources/panels/ToolBitEditor.ui b/src/Mod/Path/Gui/Resources/panels/ToolBitEditor.ui index 7642c14185..5ed0208067 100644 --- a/src/Mod/Path/Gui/Resources/panels/ToolBitEditor.ui +++ b/src/Mod/Path/Gui/Resources/panels/ToolBitEditor.ui @@ -13,202 +13,168 @@ Tool Bit Attributes - - - + + + - + 0 0 - - 0 + + Tool Bit - - - Shape - - - - - - Tool Bit + + + + + Name + + + + + + + <html><head/><body><p>Display name of the Tool Bit (initial value taken from the shape file).</p></body></html> + + + 50 + + + Display Name + + + + + + + Shape File + + + + + + + + 0 + 0 + + + + + 0 - - - - - Name - - - - - - - <html><head/><body><p>Display name of the Tool Bit (initial value taken from the shape file).</p></body></html> - - - 50 - - - Display Name - - - - - - - Shape File - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - <html><head/><body><p>The file which defines the type and shape of the Tool Bit.</p></body></html> - - - - - - - <html><head/><body><p>Change file defining type and shape of Tool Bit.</p></body></html> - - - ... - - - - - - - - - - - - - Geometry + + 0 - - - QFormLayout::AllNonFixedFieldsGrow - - - - - Point/Tip Angle - - - - - - - 0 ° - - - ° - - - - - - - Cutting Edge Height - - - - - - - 0 mm - - - mm - - - - - - - - - - - 210 - 297 - + + 0 - - Image + + 0 - - Qt::AlignCenter - - - - - - - Qt::Vertical - - - - 20 - 277 - - - - - - - - - Attributes - - - - - - - 0 - 2 - - - - - 0 - 300 - - - - QAbstractItemView::AllEditTriggers - - - true - - - - - + + + + <html><head/><body><p>The file which defines the type and shape of the Tool Bit.</p></body></html> + + + path + + + + + + + <html><head/><body><p>Change file defining type and shape of Tool Bit.</p></body></html> + + + ... + + + + + + + + + + + Parameter + + + + QFormLayout::AllNonFixedFieldsGrow + + + + + Point/Tip Angle + + + + + + + 0 ° + + + ° + + + + + + + Cutting Edge Height + + + + + + + 0 mm + + + mm + + + + + + + + + + + 210 + 297 + + + + Image + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 277 + + + + @@ -218,6 +184,13 @@
Gui/InputField.h
+ + toolName + toolCuttingEdgeAngle + toolCuttingEdgeHeight + shapePath + shapeSet + diff --git a/src/Mod/Path/PathScripts/PathToolBit.py b/src/Mod/Path/PathScripts/PathToolBit.py index fdee7aeb50..0445feabab 100644 --- a/src/Mod/Path/PathScripts/PathToolBit.py +++ b/src/Mod/Path/PathScripts/PathToolBit.py @@ -43,7 +43,9 @@ __author__ = "sliptonic (Brad Collette)" __url__ = "https://www.freecadweb.org" __doc__ = "Class to deal with and represent a tool bit." -_DebugFindTool = True +PropertyGroupShape = 'Shape' + +_DebugFindTool = False PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) PathLog.trackModule() @@ -52,15 +54,6 @@ PathLog.trackModule() def translate(context, text, disambig=None): return PySide.QtCore.QCoreApplication.translate(context, text, disambig) - -ParameterTypeConstraint = { - 'Angle': 'App::PropertyAngle', - 'Distance': 'App::PropertyLength', - 'DistanceX': 'App::PropertyLength', - 'DistanceY': 'App::PropertyLength', - 'Radius': 'App::PropertyLength'} - - def _findTool(path, typ, dbg=_DebugFindTool): PathLog.track(path) if os.path.exists(path): # absolute reference @@ -136,50 +129,16 @@ def findRelativePathTool(path): def findRelativePathLibrary(path): return _findRelativePath(path, 'Library') - -def updateConstraint(sketch, name, value): - for i, constraint in enumerate(sketch.Constraints): - if constraint.Name.split(';')[0] == name: - constr = None - if constraint.Type in ['DistanceX', 'DistanceY', 'Distance', 'Radius', 'Angle']: - constr = Sketcher.Constraint(constraint.Type, constraint.First, constraint.FirstPos, constraint.Second, constraint.SecondPos, value) - else: - print(constraint.Name, constraint.Type) - - if constr is not None: - if not PathGeom.isRoughly(constraint.Value, value.Value): - PathLog.track(name, constraint.Type, - 'update', i, "(%.2f -> %.2f)" - % (constraint.Value, value.Value)) - sketch.delConstraint(i) - sketch.recompute() - n = sketch.addConstraint(constr) - sketch.renameConstraint(n, constraint.Name) - else: - PathLog.track(name, constraint.Type, 'unchanged') - break - - -PropertyGroupBit = 'Bit' -PropertyGroupAttribute = 'Attribute' - - class ToolBit(object): def __init__(self, obj, shapeFile): PathLog.track(obj.Label, shapeFile) self.obj = obj - obj.addProperty('App::PropertyFile', 'BitShape', 'Base', - translate('PathToolBit', 'Shape for bit shape')) - obj.addProperty('App::PropertyLink', 'BitBody', 'Base', - translate('PathToolBit', - 'The parametrized body representing the tool bit')) - obj.addProperty('App::PropertyFile', 'File', 'Base', - translate('PathToolBit', 'The file of the tool')) - obj.addProperty('App::PropertyString', 'ShapeName', 'Base', - translate('PathToolBit', 'The name of the shape file')) - obj.addProperty('App::PropertyStringList', 'BitPropertyNames', 'Base', - translate('PathToolBit', 'List of all properties inherited from the bit')) + obj.addProperty('App::PropertyFile', 'BitShape', 'Base', translate('PathToolBit', 'Shape for bit shape')) + obj.addProperty('App::PropertyLink', 'BitBody', 'Base', translate('PathToolBit', 'The parametrized body representing the tool bit')) + obj.addProperty('App::PropertyFile', 'File', 'Base', translate('PathToolBit', 'The file of the tool')) + obj.addProperty('App::PropertyString', 'ShapeName', 'Base', translate('PathToolBit', 'The name of the shape file')) + obj.addProperty('App::PropertyStringList', 'BitPropertyNames', 'Base', translate('PathToolBit', 'List of all properties inherited from the bit')) if shapeFile is None: obj.BitShape = 'endmill.fcstd' @@ -200,9 +159,6 @@ class ToolBit(object): break return None - def propertyNamesAttribute(self, obj): - return [prop for prop in obj.PropertiesList if obj.getGroupOfProperty(prop) == PropertyGroupAttribute] - def onDocumentRestored(self, obj): # when files are shared it is essential to be able to change/set the shape file, # otherwise the file is hard to use @@ -213,17 +169,19 @@ class ToolBit(object): obj.setEditorMode('BitPropertyNames', 2) for prop in obj.BitPropertyNames: - obj.setEditorMode(prop, 1) - # I currently don't see why these need to be read-only - # for prop in self.propertyNamesAttribute(obj): - # obj.setEditorMode(prop, 1) + if obj.getGroupOfProperty(prop) == PropertyGroupShape: + # properties in the Shape group can only be modified while the actual + # shape is loaded, so we have to disable direct property editing + obj.setEditorMode(prop, 1) + else: + # all other custom properties can and should be edited directly in the + # property editor widget, not much value in re-implementing that + obj.setEditorMode(prop, 0) def onChanged(self, obj, prop): PathLog.track(obj.Label, prop) if prop == 'BitShape' and 'Restore' not in obj.State: self._setupBitShape(obj) - # elif obj.getGroupOfProperty(prop) == PropertyGroupBit: - # self._updateBitShape(obj, [prop]) def onDelete(self, obj, arg2=None): PathLog.track(obj.Label) @@ -335,6 +293,20 @@ class ToolBit(object): obj.BitBody = bitBody self._copyBitShape(obj) + def toolShapeProperties(self, obj): + '''toolShapeProperties(obj) ... return all properties defining the geometry''' + return sorted([prop for prop in obj.BitPropertyNames if obj.getGroupOfProperty(prop) == PropertyGroupShape]) + + def toolGroupsAndProperties(self, obj, includeShape=True): + '''toolGroupsAndProperties(obj) ... returns a dictionary of group names with a list of property names.''' + category = {} + for prop in obj.BitPropertyNames: + group = obj.getGroupOfProperty(prop) + properties = category.get(group, []) + properties.append(prop) + category[group] = properties + return category + def getBitThumbnail(self, obj): if obj.BitShape: path = findShape(obj.BitShape) @@ -359,8 +331,7 @@ class ToolBit(object): obj.File = path return True except (OSError, IOError) as e: - PathLog.error("Could not save tool {} to {} ({})".format( - obj.Label, path, e)) + PathLog.error("Could not save tool {} to {} ({})".format(obj.Label, path, e)) raise def templateAttrs(self, obj): @@ -376,12 +347,6 @@ class ToolBit(object): params[name] = PathUtil.getPropertyValueString(obj, name) attrs['parameter'] = params params = {} - for name in self.propertyNamesAttribute(obj): - if name == "UserAttributes": - for key, value in obj.UserAttributes.items(): - params[key] = value - else: - params[name] = PathUtil.getPropertyValueString(obj, name) attrs['attribute'] = params return attrs @@ -392,32 +357,6 @@ def Declaration(path): return json.load(fp) -class AttributePrototype(PathSetupSheetOpPrototype.OpPrototype): - - def __init__(self): - PathSetupSheetOpPrototype.OpPrototype.__init__(self, 'ToolBitAttribute') - self.addProperty('App::PropertyEnumeration', 'Material', - PropertyGroupAttribute, - translate('PathToolBit', 'Tool bit material')) - self.Material = ['Carbide', 'CastAlloy', 'Ceramics', 'Diamond', - 'HighCarbonToolSteel', 'HighSpeedSteel', 'Sialon'] - self.addProperty('App::PropertyDistance', 'LengthOffset', - PropertyGroupAttribute, translate('PathToolBit', - 'Length offset in Z direction')) - self.addProperty('App::PropertyInteger', 'Flutes', - PropertyGroupAttribute, translate('PathToolBit', - 'The number of flutes')) - self.addProperty('App::PropertyDistance', 'ChipLoad', - PropertyGroupAttribute, translate('PathToolBit', - 'Chipload as per manufacturer')) - self.addProperty('App::PropertyMap', 'UserAttributes', - PropertyGroupAttribute, translate('PathToolBit', - 'User Defined Values')) - self.addProperty('App::PropertyBool', 'SpindlePower', - PropertyGroupAttribute, translate('PathToolBit', - 'Whether Spindle Power should be allowed')) - - class ToolBitFactory(object): def CreateFromAttrs(self, attrs, name='ToolBit'): @@ -429,21 +368,6 @@ class ToolBitFactory(object): PathUtil.setProperty(obj, prop, params[prop]) obj.Proxy._updateBitShape(obj) obj.Proxy.unloadBitBody(obj) - params = attrs['attribute'] - proto = AttributePrototype() - uservals = {} - for pname in params: - try: - prop = proto.getProperty(pname) - prop.setupProperty(obj, pname, PropertyGroupAttribute, prop.valueFromString(params[pname])) - except Exception: - prop = proto.getProperty("UserAttributes") - uservals.update({pname: params[pname]}) - - if len(uservals.items()) > 0: - prop.setupProperty(obj, "UserAttributes", - PropertyGroupAttribute, uservals) - return obj def CreateFrom(self, path, name='ToolBit'): diff --git a/src/Mod/Path/PathScripts/PathToolBitEdit.py b/src/Mod/Path/PathScripts/PathToolBitEdit.py index b2cc823d76..5ced9249aa 100644 --- a/src/Mod/Path/PathScripts/PathToolBitEdit.py +++ b/src/Mod/Path/PathScripts/PathToolBitEdit.py @@ -24,7 +24,6 @@ import FreeCADGui import PathScripts.PathGui as PathGui import PathScripts.PathLog as PathLog import PathScripts.PathPreferences as PathPreferences -import PathScripts.PathSetupSheetGui as PathSetupSheetGui import PathScripts.PathToolBit as PathToolBit import os import re @@ -70,7 +69,6 @@ class ToolBitEditor(object): self.widgets = [] self.setupTool(self.tool) - self.setupAttributes(self.tool) def setupTool(self, tool): PathLog.track() @@ -88,7 +86,7 @@ class ToolBitEditor(object): # for all properties either assign them to existing labels and editors # or create additional ones for them if not enough have already been # created. - for nr, name in enumerate(tool.BitPropertyNames): + for nr, name in enumerate(tool.Proxy.toolShapeProperties(tool)): if nr < len(self.widgets): PathLog.debug("re-use row: {} [{}]".format(nr, name)) label, qsb, editor = self.widgets[nr] @@ -122,100 +120,11 @@ class ToolBitEditor(object): else: self.form.image.setPixmap(QtGui.QPixmap()) - def setupAttributes(self, tool): - PathLog.track() - self.proto = PathToolBit.AttributePrototype() - self.props = sorted(self.proto.properties) - self.delegate = PathSetupSheetGui.Delegate(self.form) - self.model = QtGui.QStandardItemModel(len(self.props)-1, 3, self.form) - self.model.setHorizontalHeaderLabels(['Set', 'Property', 'Value']) - - for i, name in enumerate(self.props): - PathLog.debug("propname: %s " % name) - - prop = self.proto.getProperty(name) - isset = hasattr(tool, name) - - if isset: - prop.setValue(getattr(tool, name)) - - if name == "UserAttributes": - continue - - else: - - self.model.setData(self.model.index(i, 0), isset, - QtCore.Qt.EditRole) - self.model.setData(self.model.index(i, 1), name, - QtCore.Qt.EditRole) - self.model.setData(self.model.index(i, 2), prop, - PathSetupSheetGui.Delegate.PropertyRole) - self.model.setData(self.model.index(i, 2), - prop.displayString(), - QtCore.Qt.DisplayRole) - - self.model.item(i, 0).setCheckable(True) - self.model.item(i, 0).setText('') - self.model.item(i, 1).setEditable(False) - self.model.item(i, 1).setToolTip(prop.info) - self.model.item(i, 2).setToolTip(prop.info) - - if isset: - self.model.item(i, 0).setCheckState(QtCore.Qt.Checked) - else: - self.model.item(i, 0).setCheckState(QtCore.Qt.Unchecked) - self.model.item(i, 1).setEnabled(False) - self.model.item(i, 2).setEnabled(False) - - if hasattr(tool, "UserAttributes"): - for key, value in tool.UserAttributes.items(): - PathLog.debug(key, value) - c1 = QtGui.QStandardItem() - c1.setCheckable(False) - c1.setEditable(False) - c1.setCheckState(QtCore.Qt.CheckState.Checked) - - c1.setText('') - c2 = QtGui.QStandardItem(key) - c2.setEditable(False) - c3 = QtGui.QStandardItem(value) - c3.setEditable(False) - - self.model.appendRow([c1, c2, c3]) - - self.form.attrTable.setModel(self.model) - self.form.attrTable.setItemDelegateForColumn(2, self.delegate) - self.form.attrTable.resizeColumnsToContents() - self.form.attrTable.verticalHeader().hide() - - self.model.dataChanged.connect(self.updateData) - - def updateData(self, topLeft, bottomRight): - PathLog.track() - 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) - def accept(self): PathLog.track() self.refresh() self.tool.Proxy.unloadBitBody(self.tool) - # get the attributes - for i, name in enumerate(self.props): - PathLog.debug('in accept: {}'.format(name)) - prop = self.proto.getProperty(name) - if self.model.item(i, 0) is not None: - enabled = self.model.item(i, 0).checkState() == QtCore.Qt.Checked - if enabled and not prop.getValue() is None: - prop.setupProperty(self.tool, name, - PathToolBit.PropertyGroupAttribute, - prop.getValue()) - elif hasattr(self.tool, name): - self.tool.removeProperty(name) - def reject(self): PathLog.track() self.tool.Proxy.unloadBitBody(self.tool) diff --git a/src/Mod/Path/PathScripts/PathUtil.py b/src/Mod/Path/PathScripts/PathUtil.py index 40e59a78af..7c423af22f 100644 --- a/src/Mod/Path/PathScripts/PathUtil.py +++ b/src/Mod/Path/PathScripts/PathUtil.py @@ -71,6 +71,11 @@ def getPropertyValueString(obj, prop): def setProperty(obj, prop, value): '''setProperty(obj, prop, value) ... set the property value of obj's property defined by its canonical name.''' o, attr, name = _getProperty(obj, prop) # pylint: disable=unused-variable + if attr and type(value) == str: + if type(attr) == int: + value = int(value, 0) + elif type(attr) == bool: + value = value.lower() in ['true', '1', 'yes', 'ok'] if o and name: setattr(o, name, value) diff --git a/src/Mod/Path/Tools/Shape/endmill.fcstd b/src/Mod/Path/Tools/Shape/endmill.fcstd index 4a292c5f0e..a40e5fbe51 100644 Binary files a/src/Mod/Path/Tools/Shape/endmill.fcstd and b/src/Mod/Path/Tools/Shape/endmill.fcstd differ