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 +
Gui/InputField.h
+
+
+ + +
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