Basic editor and shape update.

This commit is contained in:
Markus Lampert
2019-09-04 22:56:28 -07:00
parent dac1dd5fc5
commit 3dcc226f9e
15 changed files with 744 additions and 69 deletions

View File

@@ -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

View File

@@ -107,6 +107,7 @@
<file>panels/PointEdit.ui</file>
<file>panels/SetupGlobal.ui</file>
<file>panels/SetupOp.ui</file>
<file>panels/ToolBitEditor.ui</file>
<file>panels/ToolEditor.ui</file>
<file>panels/ToolLibraryEditor.ui</file>
<file>panels/TaskPathSimulator.ui</file>

View File

@@ -0,0 +1,170 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Form</class>
<widget class="QWidget" name="Form">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>411</width>
<height>886</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="groupBox_2">
<property name="title">
<string>Tool Bit</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="toolName">
<property name="maxLength">
<number>50</number>
</property>
<property name="placeholderText">
<string>Display Name</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Type</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QWidget" name="widget" native="true">
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="templatePath"/>
</item>
<item>
<widget class="QToolButton" name="templateSet">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="bitParams">
<property name="title">
<string>Bit Parameter</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="label_9">
<property name="text">
<string>Point/Tip Angle</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="Gui::InputField" name="toolCuttingEdgeAngle">
<property name="text">
<string>180°</string>
</property>
<property name="unit" stdset="0">
<string>°</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Cutting Edge Height</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="Gui::InputField" name="toolCuttingEdgeHeight">
<property name="text">
<string>0.00</string>
</property>
<property name="unit" stdset="0">
<string>mm</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QWidget" name="widget_2" native="true">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="image">
<property name="maximumSize">
<size>
<width>210</width>
<height>297</height>
</size>
</property>
<property name="text">
<string>Image</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Gui::InputField</class>
<extends>QLineEdit</extends>
<header>Gui/InputField.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

View File

@@ -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)

View File

@@ -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):

View File

@@ -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)

View File

@@ -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):

View File

@@ -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):

View File

@@ -0,0 +1,202 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2019 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 *
# * *
# ***************************************************************************
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

View File

@@ -0,0 +1,127 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2019 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 *
# * *
# ***************************************************************************
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)

View File

@@ -0,0 +1,153 @@
# -*- coding: utf-8 -*-
# ***************************************************************************
# * *
# * Copyright (c) 2019 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 *
# * *
# ***************************************************************************
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)

View File

@@ -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 = []