diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt
index 83937b084f..15d65c9db1 100644
--- a/src/Mod/Path/CMakeLists.txt
+++ b/src/Mod/Path/CMakeLists.txt
@@ -101,6 +101,7 @@ SET(PathScripts_SRCS
PathScripts/PathStop.py
PathScripts/PathSurface.py
PathScripts/PathSurfaceGui.py
+ PathScripts/PathToolBitEdit.py
PathScripts/PathToolController.py
PathScripts/PathToolControllerGui.py
PathScripts/PathToolEdit.py
diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc
index 19246750df..e6eae76dbc 100644
--- a/src/Mod/Path/Gui/Resources/Path.qrc
+++ b/src/Mod/Path/Gui/Resources/Path.qrc
@@ -107,6 +107,7 @@
panels/PointEdit.ui
panels/SetupGlobal.ui
panels/SetupOp.ui
+ panels/ToolBitEditor.ui
panels/ToolEditor.ui
panels/ToolLibraryEditor.ui
panels/TaskPathSimulator.ui
diff --git a/src/Mod/Path/Gui/Resources/panels/ToolBitEditor.ui b/src/Mod/Path/Gui/Resources/panels/ToolBitEditor.ui
new file mode 100644
index 0000000000..06b0ccad45
--- /dev/null
+++ b/src/Mod/Path/Gui/Resources/panels/ToolBitEditor.ui
@@ -0,0 +1,170 @@
+
+
+ Form
+
+
+
+ 0
+ 0
+ 411
+ 886
+
+
+
+ Form
+
+
+ -
+
+
+ Tool Bit
+
+
+
-
+
+
+ Name
+
+
+
+ -
+
+
+ 50
+
+
+ Display Name
+
+
+
+ -
+
+
+ Type
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+ -
+
+
+ ...
+
+
+
+
+
+
+
+
+
+ -
+
+
+ Bit Parameter
+
+
+
+ QFormLayout::AllNonFixedFieldsGrow
+
+
-
+
+
+ Point/Tip Angle
+
+
+
+ -
+
+
+ 180°
+
+
+ °
+
+
+
+ -
+
+
+ Cutting Edge Height
+
+
+
+ -
+
+
+ 0.00
+
+
+ mm
+
+
+
+
+
+
+ -
+
+
+
-
+
+
+
+ 210
+ 297
+
+
+
+ Image
+
+
+ Qt::AlignCenter
+
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+
+
+
+
+ Gui::InputField
+ QLineEdit
+
+
+
+
+
+
diff --git a/src/Mod/Path/PathScripts/PathGui.py b/src/Mod/Path/PathScripts/PathGui.py
index ed2e9e757d..98cf988106 100644
--- a/src/Mod/Path/PathScripts/PathGui.py
+++ b/src/Mod/Path/PathScripts/PathGui.py
@@ -25,6 +25,7 @@
import FreeCAD
import PathScripts.PathGeom as PathGeom
import PathScripts.PathLog as PathLog
+import PathScripts.PathUtil as PathUtil
import PySide
@@ -44,34 +45,6 @@ if LOGLEVEL:
else:
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
-def _getProperty(obj, prop):
- o = obj
- attr = obj
- name = None
- for name in prop.split('.'):
- o = attr
- if not hasattr(o, name):
- break
- attr = getattr(o, name)
-
- if o == attr:
- PathLog.warning(translate('PathGui', "%s has no property %s (%s))") % (obj.Label, prop, name))
- return (None, None, None)
-
- #PathLog.debug("found property %s of %s (%s: %s)" % (prop, obj.Label, name, attr))
- return(o, attr, name)
-
-def getProperty(obj, prop):
- '''getProperty(obj, prop) ... answer obj's property defined by its canonical name.'''
- o, attr, name = _getProperty(obj, prop) # pylint: disable=unused-variable
- return attr
-
-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 o and name:
- setattr(o, name, value)
-
def updateInputField(obj, prop, widget, onBeforeChange=None):
'''updateInputField(obj, prop, widget) ... update obj's property prop with the value of widget.
The property's value is only assigned if the new value differs from the current value.
@@ -82,13 +55,13 @@ If onBeforeChange is specified it is called before a new value is assigned to th
Returns True if a new value was assigned, False otherwise (new value is the same as the current).
'''
value = FreeCAD.Units.Quantity(widget.text()).Value
- attr = getProperty(obj, prop)
+ attr = PathUtil.getProperty(obj, prop)
attrValue = attr.Value if hasattr(attr, 'Value') else attr
if not PathGeom.isRoughly(attrValue, value):
PathLog.debug("updateInputField(%s, %s): %.2f -> %.2f" % (obj.Label, prop, attr, value))
if onBeforeChange:
onBeforeChange(obj)
- setProperty(obj, prop, value)
+ PathUtil.setProperty(obj, prop, value)
return True
return False
@@ -107,7 +80,7 @@ The spin box gets bound to a given property and supports update in both directio
self.widget = widget
self.prop = prop
self.onBeforeChange = onBeforeChange
- attr = getProperty(self.obj, self.prop)
+ attr = PathUtil.getProperty(self.obj, self.prop)
if attr is not None:
if hasattr(attr, 'Value'):
widget.setProperty('unit', attr.getUserPreferred()[2])
@@ -134,7 +107,7 @@ If no value is provided the value of the bound property is used.
quantity can be of type Quantity or Float.'''
if self.valid:
if quantity is None:
- quantity = getProperty(self.obj, self.prop)
+ quantity = PathUtil.getProperty(self.obj, self.prop)
value = quantity.Value if hasattr(quantity, 'Value') else quantity
self.widget.setProperty('rawValue', value)
diff --git a/src/Mod/Path/PathScripts/PathMillFace.py b/src/Mod/Path/PathScripts/PathMillFace.py
index 362fd18d17..cb6c02f080 100644
--- a/src/Mod/Path/PathScripts/PathMillFace.py
+++ b/src/Mod/Path/PathScripts/PathMillFace.py
@@ -80,7 +80,8 @@ class ObjectFace(PathPocketBase.ObjectPocket):
# default depths calculation not correct for facing
if prop == "Base":
job = PathUtils.findParentJob(obj)
- obj.OpStartDepth = job.Stock.Shape.BoundBox.ZMax
+ if job:
+ obj.OpStartDepth = job.Stock.Shape.BoundBox.ZMax
if len(obj.Base) >= 1:
print('processing')
@@ -95,7 +96,7 @@ class ObjectFace(PathPocketBase.ObjectPocket):
# Otherwise, top of part.
obj.OpFinalDepth = Part.makeCompound(sublist).BoundBox.ZMax
- else:
+ elif job:
obj.OpFinalDepth = job.Proxy.modelBoundBox(job).ZMax
def areaOpShapes(self, obj):
diff --git a/src/Mod/Path/PathScripts/PathSetupSheetGui.py b/src/Mod/Path/PathScripts/PathSetupSheetGui.py
index 1537f870c2..5b5a75b2d6 100644
--- a/src/Mod/Path/PathScripts/PathSetupSheetGui.py
+++ b/src/Mod/Path/PathScripts/PathSetupSheetGui.py
@@ -306,9 +306,9 @@ class GlobalEditor(object):
def getFields(self):
def updateExpression(name, widget):
value = str(widget.text())
- val = PathGui.getProperty(self.obj, name)
+ val = PathUtil.getProperty(self.obj, name)
if val != value:
- PathGui.setProperty(self.obj, name, value)
+ PathUtil.setProperty(self.obj, name, value)
updateExpression('StartDepthExpression', self.form.setupStartDepthExpr)
updateExpression('FinalDepthExpression', self.form.setupFinalDepthExpr)
diff --git a/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py b/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py
index 146d70821a..8c5c3e4b8d 100644
--- a/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py
+++ b/src/Mod/Path/PathScripts/PathSetupSheetOpPrototype.py
@@ -106,6 +106,10 @@ class PropertyQuantity(Property):
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"
@@ -137,24 +141,25 @@ class PropertyString(Property):
class OpPrototype(object):
PropertyType = {
- 'App::PropertyBool': PropertyBool,
- 'App::PropertyDistance': PropertyDistance,
- 'App::PropertyEnumeration': PropertyEnumeration,
- '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::PropertyPercent': PropertyPercent,
- 'App::PropertyString': PropertyString,
- 'App::PropertyStringList': Property,
- 'App::PropertyVectorDistance': Property,
- 'App::PropertyVectorList': Property,
- 'Part::PropertyPartShape': Property,
+ 'App::PropertyAngle': PropertyAngle,
+ 'App::PropertyBool': PropertyBool,
+ 'App::PropertyDistance': PropertyDistance,
+ 'App::PropertyEnumeration': PropertyEnumeration,
+ '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::PropertyPercent': PropertyPercent,
+ 'App::PropertyString': PropertyString,
+ 'App::PropertyStringList': Property,
+ 'App::PropertyVectorDistance': Property,
+ 'App::PropertyVectorList': Property,
+ 'Part::PropertyPartShape': Property,
}
def __init__(self, name):
diff --git a/src/Mod/Path/PathScripts/PathSetupSheetOpPrototypeGui.py b/src/Mod/Path/PathScripts/PathSetupSheetOpPrototypeGui.py
index c215d0057d..f89b7b40ed 100644
--- a/src/Mod/Path/PathScripts/PathSetupSheetOpPrototypeGui.py
+++ b/src/Mod/Path/PathScripts/PathSetupSheetOpPrototypeGui.py
@@ -112,6 +112,21 @@ class _PropertyStringEditor(_PropertyEditor):
def setModelData(self, widget):
self.prop.setValue(widget.text())
+class _PropertyAngleEditor(_PropertyEditor):
+ '''Editor for angle values - uses a line edit'''
+
+ def widget(self, parent):
+ return QtGui.QLineEdit(parent)
+
+ def setEditorData(self, widget):
+ quantity = self.prop.getValue()
+ if quantity is None:
+ quantity = FreeCAD.Units.Quantity(0, FreeCAD.Units.Angle)
+ widget.setText(quantity.getUserPreferred()[0])
+
+ def setModelData(self, widget):
+ self.prop.setValue(FreeCAD.Units.Quantity(widget.text()))
+
class _PropertyLengthEditor(_PropertyEditor):
'''Editor for length values - uses a line edit.'''
@@ -174,15 +189,16 @@ class _PropertyFloatEditor(_PropertyEditor):
self.prop.setValue(widget.value())
_EditorFactory = {
- PathSetupSheetOpPrototype.Property: None,
- PathSetupSheetOpPrototype.PropertyBool: _PropertyBoolEditor,
- PathSetupSheetOpPrototype.PropertyDistance: _PropertyLengthEditor,
- PathSetupSheetOpPrototype.PropertyEnumeration: _PropertyEnumEditor,
- PathSetupSheetOpPrototype.PropertyFloat: _PropertyFloatEditor,
- PathSetupSheetOpPrototype.PropertyInteger: _PropertyIntegerEditor,
- PathSetupSheetOpPrototype.PropertyLength: _PropertyLengthEditor,
- PathSetupSheetOpPrototype.PropertyPercent: _PropertyPercentEditor,
- PathSetupSheetOpPrototype.PropertyString: _PropertyStringEditor,
+ PathSetupSheetOpPrototype.Property: None,
+ PathSetupSheetOpPrototype.PropertyAngle: _PropertyAngleEditor,
+ PathSetupSheetOpPrototype.PropertyBool: _PropertyBoolEditor,
+ PathSetupSheetOpPrototype.PropertyDistance: _PropertyLengthEditor,
+ PathSetupSheetOpPrototype.PropertyEnumeration: _PropertyEnumEditor,
+ PathSetupSheetOpPrototype.PropertyFloat: _PropertyFloatEditor,
+ PathSetupSheetOpPrototype.PropertyInteger: _PropertyIntegerEditor,
+ PathSetupSheetOpPrototype.PropertyLength: _PropertyLengthEditor,
+ PathSetupSheetOpPrototype.PropertyPercent: _PropertyPercentEditor,
+ PathSetupSheetOpPrototype.PropertyString: _PropertyStringEditor,
}
def Editor(prop):
diff --git a/src/Mod/Path/PathScripts/PathToolBit.py b/src/Mod/Path/PathScripts/PathToolBit.py
new file mode 100644
index 0000000000..03f400f449
--- /dev/null
+++ b/src/Mod/Path/PathScripts/PathToolBit.py
@@ -0,0 +1,202 @@
+# -*- coding: utf-8 -*-
+
+# ***************************************************************************
+# * *
+# * Copyright (c) 2019 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 Part
+import PathScripts.PathGeom as PathGeom
+import PathScripts.PathLog as PathLog
+import PathScripts.PathSetupSheetOpPrototype as PathSetupSheetOpPrototype
+import PathScripts.PathUtil as PathUtil
+import PySide
+import Sketcher
+import math
+import zipfile
+
+__title__ = "Tool bits."
+__author__ = "sliptonic (Brad Collette)"
+__url__ = "http://www.freecadweb.org"
+__doc__ = "Class to deal with and represent a tool bit."
+
+PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
+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 updateConstraint(sketch, name, value):
+ for i, constraint in enumerate(sketch.Constraints):
+ if constraint.Name.split(';')[0] == name:
+ if constraint.Type in ['DistanceX', 'DistanceY', 'Distance', 'Radius']:
+ if not PathGeom.isRoughly(constraint.Value, value.Value):
+ PathLog.track(name, constraint.Type, 'update', i)
+ constr = Sketcher.Constraint(constraint.Type, constraint.First, value)
+ sketch.delConstraint(i)
+ sketch.recompute()
+ n = sketch.addConstraint(constr)
+ sketch.renameConstraint(n, constraint.Name)
+ else:
+ PathLog.track(name, constraint.Type, 'unchanged')
+ else:
+ print(constraint.Name, constraint.Type)
+ break
+
+PropertyGroupBit = 'Bit'
+
+class ToolBit(object):
+
+ def __init__(self, obj, templateFile):
+ PathLog.track(obj.Label, templateFile)
+ self.obj = obj
+ obj.addProperty('App::PropertyFile', 'BitTemplate', 'Base', translate('PathToolBit', 'Template for bit shape'))
+ obj.addProperty('App::PropertyLink', 'BitBody', 'Base', translate('PathToolBit', 'The parametrized body representing the tool bit'))
+ if templateFile is not None:
+ obj.BitTemplate = templateFile
+ self._setupBitFromTemplate(obj)
+ self.onDocumentRestored(obj)
+
+ 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
+ break
+ return None
+
+ def bitPropertyNames(self, obj):
+ return [prop for prop in obj.PropertiesList if obj.getGroupOfProperty(prop) == PropertyGroupBit]
+
+ def onDocumentRestored(self, obj):
+ obj.setEditorMode('BitTemplate', 1)
+ obj.setEditorMode('BitBody', 2)
+ obj.setEditorMode('Shape', 2)
+
+ for prop in self.bitPropertyNames(obj):
+ obj.setEditorMode(prop, 1)
+
+ def onChanged(self, obj, prop):
+ PathLog.track(obj.Label, prop)
+ if prop == 'BitTemplate' and not 'Restore' in obj.State:
+ self._setupBitFromTemplate(obj)
+ #elif obj.getGroupOfProperty(prop) == PropertyGroupBit:
+ # self._updateBitShape(obj, [prop])
+
+ def _updateBitShape(self, obj, properties=None):
+ if not properties:
+ properties = self.bitPropertyNames(obj)
+ for prop in properties:
+ for sketch in [o for o in obj.BitBody.Group if o.TypeId == 'Sketcher::SketchObject']:
+ PathLog.track(obj.Label, sketch.Label, prop)
+ updateConstraint(sketch, prop, obj.getPropertyByName(prop))
+ self._copyBitShape(obj)
+
+ def _copyBitShape(self, obj):
+ obj.Document.recompute()
+ if obj.BitBody and obj.BitBody.Shape:
+ obj.Shape = obj.BitBody.Shape
+ else:
+ obj.Shape = Part.Shape()
+
+ def _loadBitBody(self, obj, path=None):
+ if not path:
+ path = obj.BitTemplate
+ docOpened = False
+ doc = None
+ for d in FreeCAD.listDocuments():
+ if FreeCAD.getDocument(d).FileName == path:
+ doc = FreeCAD.getDocument(d)
+ break
+ if doc is None:
+ doc = FreeCAD.open(path)
+ docOpened = True
+ return (doc, docOpened)
+
+ def _removeBitBody(self, obj):
+ if obj.BitBody:
+ obj.BitBody.removeObjectsFromDocument()
+ obj.Document.removeObject(obj.BitBody.Name)
+ obj.BitBody = None
+
+ def _deleteBitSetup(self, obj):
+ PathLog.track(obj.Label)
+ self._removeBitBody(obj)
+ self._copyBitShape(obj)
+ for prop in self.bitPropertyNames(obj):
+ obj.removeProperty(prop)
+
+ def _setupBitFromTemplate(self, obj, path=None):
+ (doc, docOpened) = self._loadBitBody(obj, path)
+
+ obj.Label = doc.RootObjects[0].Label
+ self._deleteBitSetup(obj)
+ obj.BitBody = obj.Document.copyObject(doc.RootObjects[0], True)
+ if docOpened:
+ FreeCAD.closeDocument(doc.Name)
+
+ if obj.BitBody.ViewObject:
+ obj.BitBody.ViewObject.Visibility = False
+ self._copyBitShape(obj)
+
+ for sketch in [o for o in obj.BitBody.Group if o.TypeId == 'Sketcher::SketchObject']:
+ for constraint in [c for c in sketch.Constraints if c.Name != '']:
+ typ = ParameterTypeConstraint.get(constraint.Type)
+ PathLog.track(constraint, typ)
+ if typ is not None:
+ parts = [p.strip() for p in constraint.Name.split(';')]
+ prop = parts[0]
+ desc = ''
+ if len(parts) > 1:
+ desc = parts[1]
+ obj.addProperty(typ, prop, PropertyGroupBit, desc)
+ value = constraint.Value
+ if constraint.Type == 'Angle':
+ value = value * 180 / math.pi
+ PathUtil.setProperty(obj, prop, constraint.Value)
+
+ def getBitThumbnail(self, obj):
+ if obj.BitTemplate:
+ with open(obj.BitTemplate, 'rb') as fd:
+ zf = zipfile.ZipFile(fd)
+ pf = zf.open('thumbnails/Thumbnail.png', 'r')
+ data = pf.read()
+ pf.close()
+ return data
+ else:
+ return None
+
+
+def Create(name = 'ToolBit', templateFile=None):
+ obj = FreeCAD.ActiveDocument.addObject('Part::FeaturePython', name)
+ obj.Proxy = ToolBit(obj, templateFile)
+ return obj
diff --git a/src/Mod/Path/PathScripts/PathToolBitEdit.py b/src/Mod/Path/PathScripts/PathToolBitEdit.py
new file mode 100644
index 0000000000..f2fe552580
--- /dev/null
+++ b/src/Mod/Path/PathScripts/PathToolBitEdit.py
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+
+# ***************************************************************************
+# * *
+# * Copyright (c) 2019 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 Path
+import PathScripts.PathGui as PathGui
+import PathScripts.PathLog as PathLog
+import PathScripts.PathToolBit as PathToolBit
+import copy
+import math
+import re
+
+from PySide import QtGui
+
+PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
+PathLog.trackModule(PathLog.thisModule())
+
+
+ParameterTypeConstraint = {
+ 'Angle': 'Angle',
+ 'Distance': 'Length',
+ 'DistanceX': 'Length',
+ 'DistanceY': 'Length',
+ 'Radius': 'Length'
+ }
+
+ParameterTypeProperty = {
+ 'Length': 'App::PropertyLength',
+ 'Angle': 'App::PropertyAngle'
+ }
+
+class ToolBitEditor:
+ '''UI and controller for editing a ToolBit.
+ The controller embeds the UI to the parentWidget which has to have a layout attached to it.
+ '''
+
+ def __init__(self, tool, parentWidget=None):
+ self.form = FreeCADGui.PySideUic.loadUi(":/panels/ToolBitEditor.ui")
+
+ if parentWidget:
+ self.form.setParent(parentWidget)
+ parentWidget.layout().addWidget(self.form)
+
+ self.tool = tool
+ if not tool.BitTemplate:
+ self.tool.BitTemplate = 'src/Mod/Path/Tools/Template/endmill-straight.fcstd'
+ self.setupTool(self.tool)
+
+ def setupTool(self, tool):
+ layout = self.form.bitParams.layout()
+ for i in range(layout.rowCount() - 1, -1, -1):
+ layout.removeRow(i)
+ editor = {}
+ ui = FreeCADGui.UiLoader()
+ for name in tool.PropertiesList:
+ if tool.getGroupOfProperty(name) == PathToolBit.PropertyGroupBit:
+ qsb = ui.createWidget('Gui::QuantitySpinBox')
+ editor[name] = PathGui.QuantitySpinBox(qsb, tool, name)
+ label = QtGui.QLabel(re.sub('([A-Z][a-z]+)', r' \1', re.sub('([A-Z]+)', r' \1', name)))
+ #if parameter.get('Desc'):
+ # qsb.setToolTip(parameter['Desc'])
+ layout.addRow(label, qsb)
+ self.bitEditor = editor
+
+ def accept(self):
+ self.refresh()
+
+ def reject(self):
+ pass
+
+ def updateUI(self):
+ PathLog.track()
+ self.form.toolName.setText(self.tool.Label)
+ self.form.templatePath.setText(self.tool.BitTemplate)
+
+ for editor in self.bitEditor:
+ self.bitEditor[editor].updateSpinBox()
+
+ def updateTemplate(self):
+ self.tool.BitTemplate = str(self.form.templatePath.text())
+ self.setupTool(self.tool)
+
+ def updateTool(self):
+ PathLog.track()
+ self.tool.Label = str(self.form.toolName.text())
+ self.tool.BitTemplate = str(self.form.templatePath.text())
+
+ for editor in self.bitEditor:
+ self.bitEditor[editor].updateProperty()
+
+ self.tool.Proxy._updateBitShape(self.tool)
+
+ def refresh(self):
+ PathLog.track()
+ self.form.blockSignals(True)
+ self.updateTool()
+ self.updateUI()
+ self.form.blockSignals(False)
+
+ def setupUI(self):
+ PathLog.track()
+ self.updateUI()
+
+ self.form.toolName.editingFinished.connect(self.refresh)
+ self.form.templatePath.editingFinished.connect(self.updateTemplate)
diff --git a/src/Mod/Path/PathScripts/PathToolBitGui.py b/src/Mod/Path/PathScripts/PathToolBitGui.py
new file mode 100644
index 0000000000..cec2d86980
--- /dev/null
+++ b/src/Mod/Path/PathScripts/PathToolBitGui.py
@@ -0,0 +1,153 @@
+# -*- coding: utf-8 -*-
+
+# ***************************************************************************
+# * *
+# * Copyright (c) 2019 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.PathToolBit as PathToolBit
+import PathScripts.PathToolBitEdit as PathToolBitEdit
+import PathScripts.PathUtil as PathUtil
+
+from PySide import QtCore, QtGui
+
+__title__ = "Tool Bit UI"
+__author__ = "sliptonic (Brad Collette)"
+__url__ = "http://www.freecadweb.org"
+__doc__ = "Task panel editor for a ToolBit"
+
+# Qt translation handling
+def translate(context, text, disambig=None):
+ return QtCore.QCoreApplication.translate(context, text, disambig)
+
+PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
+PathLog.trackModule(PathLog.thisModule())
+
+class ViewProvider(object):
+ '''ViewProvider for a ToolBit.
+ It's sole job is to provide an icon and invoke the TaskPanel on edit.'''
+
+ def __init__(self, vobj, name):
+ PathLog.track(name, vobj.Object)
+ self.icon = name
+ self.obj = vobj.Object
+ self.vobj = vobj
+ vobj.Proxy = self
+
+ def attach(self, vobj):
+ PathLog.track(vobj.Object)
+ self.vobj = vobj
+ self.obj = vobj.Object
+
+ def getIcon(self):
+ png = self.obj.Proxy.getBitThumbnail(self.obj)
+ if png:
+ pixmap = QtGui.QPixmap()
+ pixmap.loadFromData(png, "PNG")
+ return QtGui.QIcon(pixmap)
+ return ':/icons/Path-ToolChange.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):
+ if self.obj.BitBody:
+ return [self.obj.BitBody]
+ return []
+
+ def doubleClicked(self, vobj):
+ self.setEdit(vobj)
+
+class TaskPanel:
+ '''TaskPanel for the SetupSheet - if it is being edited directly.'''
+
+ def __init__(self, vobj):
+ PathLog.track(vobj.Object.Label)
+ self.vobj = vobj
+ self.obj = vobj.Object
+ self.editor = PathToolBitEdit.ToolBitEditor(self.obj)
+ self.form = self.editor.form
+ FreeCAD.ActiveDocument.openTransaction(translate("PathToolBit", "Edit ToolBit"))
+
+ def reject(self):
+ self.editor.reject()
+ FreeCAD.ActiveDocument.abortTransaction()
+ FreeCADGui.Control.closeDialog()
+ FreeCAD.ActiveDocument.recompute()
+
+ def accept(self):
+ self.editor.accept()
+
+ FreeCAD.ActiveDocument.commitTransaction()
+ FreeCADGui.ActiveDocument.resetEdit()
+ FreeCADGui.Control.closeDialog()
+ FreeCAD.ActiveDocument.recompute()
+
+ def getFields(self):
+ self.editor.getFields()
+
+ def updateUI(self):
+ self.editor.updateUI()
+
+ def updateModel(self):
+ self.editor.updateTool()
+ FreeCAD.ActiveDocument.recompute()
+
+ def setFields(self):
+ self.editor.setFields()
+
+ def setupUi(self):
+ self.editor.setupUI()
+
+def Create(name = 'ToolBit'):
+ '''Create(name = 'ToolBit') ... creates a new tool bit'''
+ FreeCAD.ActiveDocument.openTransaction(translate("PathToolBit", "Create ToolBit"))
+ tool = PathToolBit.Create(name)
+ PathIconViewProvider.Attach(tool.ViewObject, name)
+ return tool
+
+PathIconViewProvider.RegisterViewProvider('ToolBit', ViewProvider)
diff --git a/src/Mod/Path/PathScripts/PathUtil.py b/src/Mod/Path/PathScripts/PathUtil.py
index 3fa449106c..9633c6833d 100644
--- a/src/Mod/Path/PathScripts/PathUtil.py
+++ b/src/Mod/Path/PathScripts/PathUtil.py
@@ -34,14 +34,40 @@ other than PathLog, then it probably doesn't belong here.
import six
import PathScripts.PathLog as PathLog
+import PySide
-LOGLEVEL = False
+PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
-if LOGLEVEL:
- PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
- PathLog.trackModule(PathLog.thisModule())
-else:
- PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
+def translate(context, text, disambig=None):
+ return PySide.QtCore.QCoreApplication.translate(context, text, disambig)
+
+def _getProperty(obj, prop):
+ o = obj
+ attr = obj
+ name = None
+ for name in prop.split('.'):
+ o = attr
+ if not hasattr(o, name):
+ break
+ attr = getattr(o, name)
+
+ if o == attr:
+ PathLog.warning(translate('PathGui', "%s has no property %s (%s))") % (obj.Label, prop, name))
+ return (None, None, None)
+
+ #PathLog.debug("found property %s of %s (%s: %s)" % (prop, obj.Label, name, attr))
+ return(o, attr, name)
+
+def getProperty(obj, prop):
+ '''getProperty(obj, prop) ... answer obj's property defined by its canonical name.'''
+ o, attr, name = _getProperty(obj, prop) # pylint: disable=unused-variable
+ return attr
+
+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 o and name:
+ setattr(o, name, value)
# NotValidBaseTypeIds = ['Sketcher::SketchObject']
NotValidBaseTypeIds = []
diff --git a/src/Mod/Path/Tools/Template/drill-straight.fcstd b/src/Mod/Path/Tools/Template/drill-straight.fcstd
index 0e33b9a2d5..1de5f44181 100644
Binary files a/src/Mod/Path/Tools/Template/drill-straight.fcstd and b/src/Mod/Path/Tools/Template/drill-straight.fcstd differ
diff --git a/src/Mod/Path/Tools/Template/endmill-straight.fcstd b/src/Mod/Path/Tools/Template/endmill-straight.fcstd
index 696296467b..17d6be48f4 100644
Binary files a/src/Mod/Path/Tools/Template/endmill-straight.fcstd and b/src/Mod/Path/Tools/Template/endmill-straight.fcstd differ
diff --git a/src/Mod/Path/Tools/Template/v-bit.fcstd b/src/Mod/Path/Tools/Template/v-bit.fcstd
index 3fb173c4b6..b937043666 100644
Binary files a/src/Mod/Path/Tools/Template/v-bit.fcstd and b/src/Mod/Path/Tools/Template/v-bit.fcstd differ