Move Path.Base and Path.Base.Gui refactoring
This commit is contained in:
@@ -25,9 +25,9 @@ from pivy import coin
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Path
|
||||
import Path.Base.Gui.Util as PathGuiUtil
|
||||
import Path.Op.Gui.Base as PathOpGui
|
||||
import PathScripts.PathFeatureExtensions as FeatureExtensions
|
||||
import PathScripts.PathGui as PathGui
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
@@ -197,7 +197,7 @@ class TaskPanelExtensionPage(PathOpGui.TaskPanelPage):
|
||||
|
||||
self.extensions = list()
|
||||
|
||||
self.defaultLength = PathGui.QuantitySpinBox(
|
||||
self.defaultLength = PathGuiUtil.QuantitySpinBox(
|
||||
self.form.defaultLength, obj, "ExtensionLengthDefault"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,262 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2017 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
|
||||
|
||||
# lazily loaded modules
|
||||
from lazy_loader.lazy_loader import LazyLoader
|
||||
|
||||
Draft = LazyLoader("Draft", globals(), "Draft")
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
from pivy import coin
|
||||
|
||||
__title__ = "Path GetPoint UI"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Helper class to use FreeCADGUi.Snapper to let the user enter arbitrary points while the task panel is active."
|
||||
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
# Path.Log.track(Path.Log.thisModule())
|
||||
|
||||
|
||||
class TaskPanel:
|
||||
"""Use an instance of this class in another TaskPanel to invoke the snapper.
|
||||
Create the instance in the TaskPanel's constructors and invoke getPoint(whenDone, start) whenever a new point is
|
||||
required or an existing point needs to be edited. The receiver is expected to have the same lifespan as the form
|
||||
provided in the constructor.
|
||||
The (only) public API function other than the constructor is getPoint(whenDone, start).
|
||||
"""
|
||||
|
||||
def __init__(self, form, onPath=False):
|
||||
"""__init___(form) ... form will be replaced by PointEdit.ui while the Snapper is active."""
|
||||
self.formOrig = form
|
||||
self.formPoint = FreeCADGui.PySideUic.loadUi(":/panels/PointEdit.ui")
|
||||
|
||||
self.formPoint.setParent(form.parent())
|
||||
form.parent().layout().addWidget(self.formPoint)
|
||||
self.formPoint.hide()
|
||||
|
||||
self.setupUi()
|
||||
self.buttonBox = None
|
||||
self.onPath = onPath
|
||||
self.obj = None
|
||||
|
||||
self.pt = None
|
||||
self.point = None
|
||||
self.pointCbClick = None
|
||||
self.pointCbMove = None
|
||||
self.pointWhenDone = None
|
||||
self.escape = None
|
||||
self.view = None
|
||||
|
||||
def setupUi(self):
|
||||
"""setupUi() ... internal function - do not call."""
|
||||
self.formPoint.buttonBox.accepted.connect(self.pointAccept)
|
||||
self.formPoint.buttonBox.rejected.connect(self.pointReject)
|
||||
|
||||
self.formPoint.globalX.editingFinished.connect(self.updatePoint)
|
||||
self.formPoint.globalY.editingFinished.connect(self.updatePoint)
|
||||
self.formPoint.globalZ.editingFinished.connect(self.updatePoint)
|
||||
|
||||
self.formPoint.globalX.setProperty(
|
||||
"unit", FreeCAD.Units.MilliMetre.getUserPreferred()[2]
|
||||
)
|
||||
self.formPoint.globalY.setProperty(
|
||||
"unit", FreeCAD.Units.MilliMetre.getUserPreferred()[2]
|
||||
)
|
||||
self.formPoint.globalZ.setProperty(
|
||||
"unit", FreeCAD.Units.MilliMetre.getUserPreferred()[2]
|
||||
)
|
||||
|
||||
def addEscapeShortcut(self):
|
||||
"""addEscapeShortcut() ... internal function - do not call."""
|
||||
# The only way I could get to intercept the escape key, or really any key was
|
||||
# by creating an action with a shortcut .....
|
||||
self.escape = QtGui.QAction(self.formPoint)
|
||||
self.escape.setText("Done")
|
||||
self.escape.setShortcut(QtGui.QKeySequence.fromString("Esc"))
|
||||
QtCore.QObject.connect(
|
||||
self.escape, QtCore.SIGNAL("triggered()"), self.pointDone
|
||||
)
|
||||
self.formPoint.addAction(self.escape)
|
||||
|
||||
def removeEscapeShortcut(self):
|
||||
"""removeEscapeShortcut() ... internal function - do not call."""
|
||||
if self.escape:
|
||||
self.formPoint.removeAction(self.escape)
|
||||
self.escape = None
|
||||
|
||||
def getPoint(self, whenDone, start=None):
|
||||
"""getPoint(whenDone, start=None) ... invoke Snapper and call whenDone when a point is entered or the user cancels the operation.
|
||||
whenDone(point, obj) is called either with a point and the object on which the point lies if the user set the point,
|
||||
or None and None if the user cancelled the operation.
|
||||
start is an optional Vector indicating from where to start Snapper. This is mostly used when editing existing points. Snapper also
|
||||
creates a dotted line indicating from where the original point started from.
|
||||
If start is specified the Snapper UI is closed on the first point the user enters. If start remains None, then Snapper is kept open
|
||||
until the user explicitly closes Snapper. This lets the user enter multiple points in quick succession."""
|
||||
|
||||
# there's no get point without Snapper, if it's not loaded, need to do that explicitly
|
||||
if not hasattr(FreeCADGui, "Snapper"):
|
||||
import DraftTools
|
||||
|
||||
def displayPoint(p):
|
||||
self.point = p
|
||||
self.formPoint.globalX.setProperty("rawValue", p.x)
|
||||
self.formPoint.globalY.setProperty("rawValue", p.y)
|
||||
self.formPoint.globalZ.setProperty("rawValue", p.z)
|
||||
self.formPoint.globalX.setFocus()
|
||||
self.formPoint.globalX.selectAll()
|
||||
|
||||
def mouseMove(cb):
|
||||
p = None
|
||||
event = cb.getEvent()
|
||||
pos = event.getPosition()
|
||||
if self.onPath:
|
||||
# There should not be a dependency from Draft->Path, so handle Path "snapping"
|
||||
# directly, at least for now. Simple enough because there isn't really any
|
||||
# "snapping" going on other than what getObjectInfo() provides.
|
||||
screenpos = tuple(pos.getValue())
|
||||
snapInfo = Draft.get3DView().getObjectInfo(screenpos)
|
||||
if snapInfo:
|
||||
obj = FreeCAD.ActiveDocument.getObject(snapInfo["Object"])
|
||||
if hasattr(obj, "Path"):
|
||||
self.obj = obj
|
||||
p = FreeCAD.Vector(snapInfo["x"], snapInfo["y"], snapInfo["z"])
|
||||
self.pt = p
|
||||
else:
|
||||
self.obj = None
|
||||
else:
|
||||
# Snapper handles regular objects just fine
|
||||
cntrl = event.wasCtrlDown()
|
||||
shift = event.wasShiftDown()
|
||||
self.pt = FreeCADGui.Snapper.snap(
|
||||
pos, lastpoint=start, active=cntrl, constrain=shift
|
||||
)
|
||||
plane = FreeCAD.DraftWorkingPlane
|
||||
p = plane.getLocalCoords(self.pt)
|
||||
self.obj = FreeCADGui.Snapper.lastSnappedObject
|
||||
|
||||
if p:
|
||||
displayPoint(p)
|
||||
|
||||
def click(cb):
|
||||
event = cb.getEvent()
|
||||
if (
|
||||
event.getButton() == 1
|
||||
and event.getState() == coin.SoMouseButtonEvent.DOWN
|
||||
):
|
||||
if self.obj:
|
||||
accept()
|
||||
|
||||
def accept():
|
||||
if start:
|
||||
self.pointAccept()
|
||||
else:
|
||||
self.pointAcceptAndContinue()
|
||||
|
||||
def cancel():
|
||||
self.pointReject()
|
||||
|
||||
self.pointWhenDone = whenDone
|
||||
self.formOrig.hide()
|
||||
self.formPoint.show()
|
||||
self.addEscapeShortcut()
|
||||
if start:
|
||||
displayPoint(start)
|
||||
else:
|
||||
displayPoint(FreeCAD.Vector(0, 0, 0))
|
||||
|
||||
self.view = Draft.get3DView()
|
||||
self.pointCbClick = self.view.addEventCallbackPivy(
|
||||
coin.SoMouseButtonEvent.getClassTypeId(), click
|
||||
)
|
||||
self.pointCbMove = self.view.addEventCallbackPivy(
|
||||
coin.SoLocation2Event.getClassTypeId(), mouseMove
|
||||
)
|
||||
|
||||
if self.buttonBox:
|
||||
self.buttonBox.setEnabled(False)
|
||||
|
||||
FreeCADGui.Snapper.forceGridOff = True
|
||||
|
||||
def pointFinish(self, ok, cleanup=True):
|
||||
"""pointFinish(ok, cleanup=True) ... internal function - do not call."""
|
||||
|
||||
if cleanup:
|
||||
self.removeGlobalCallbacks()
|
||||
FreeCADGui.Snapper.off(True)
|
||||
if self.buttonBox:
|
||||
self.buttonBox.setEnabled(True)
|
||||
self.removeEscapeShortcut()
|
||||
self.formPoint.hide()
|
||||
self.formOrig.show()
|
||||
self.formOrig.setFocus()
|
||||
|
||||
if ok:
|
||||
self.updatePoint(False)
|
||||
self.pointWhenDone(self.pt, self.obj)
|
||||
else:
|
||||
self.pointWhenDone(None, None)
|
||||
|
||||
def pointDone(self):
|
||||
"""pointDone() ... internal function - do not call."""
|
||||
self.pointFinish(False)
|
||||
|
||||
def pointReject(self):
|
||||
"""pointReject() ... internal function - do not call."""
|
||||
self.pointFinish(False)
|
||||
|
||||
def pointAccept(self):
|
||||
"""pointAccept() ... internal function - do not call."""
|
||||
self.pointFinish(True)
|
||||
|
||||
def pointAcceptAndContinue(self):
|
||||
"""pointAcceptAndContinue() ... internal function - do not call."""
|
||||
self.pointFinish(True, False)
|
||||
|
||||
def removeGlobalCallbacks(self):
|
||||
"""removeGlobalCallbacks() ... internal function - do not call."""
|
||||
if hasattr(self, "view") and self.view:
|
||||
if self.pointCbClick:
|
||||
self.view.removeEventCallbackPivy(
|
||||
coin.SoMouseButtonEvent.getClassTypeId(), self.pointCbClick
|
||||
)
|
||||
self.pointCbClick = None
|
||||
if self.pointCbMove:
|
||||
self.view.removeEventCallbackPivy(
|
||||
coin.SoLocation2Event.getClassTypeId(), self.pointCbMove
|
||||
)
|
||||
self.pointCbMove = None
|
||||
self.view = None
|
||||
|
||||
def updatePoint(self, usePoint=True):
|
||||
"""updatePoint() ... internal function - do not call."""
|
||||
if usePoint and self.point:
|
||||
self.pt = self.point
|
||||
else:
|
||||
x = FreeCAD.Units.Quantity(self.formPoint.globalX.text()).Value
|
||||
y = FreeCAD.Units.Quantity(self.formPoint.globalY.text()).Value
|
||||
z = FreeCAD.Units.Quantity(self.formPoint.globalZ.text()).Value
|
||||
self.pt = FreeCAD.Vector(x, y, z)
|
||||
@@ -1,253 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2017 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 Path.Base.Util as PathUtil
|
||||
from PySide import QtGui, QtCore
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
__title__ = "Path UI helper and utility functions"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "A collection of helper and utility functions for the Path GUI."
|
||||
|
||||
|
||||
if False:
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
else:
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
|
||||
|
||||
def populateCombobox(form, enumTups, comboBoxesPropertyMap):
|
||||
"""populateCombobox(form, enumTups, comboBoxesPropertyMap) ... populate comboboxes with translated enumerations
|
||||
** comboBoxesPropertyMap will be unnecessary if UI files use strict combobox naming protocol.
|
||||
Args:
|
||||
form = UI form
|
||||
enumTups = list of (translated_text, data_string) tuples
|
||||
comboBoxesPropertyMap = list of (translated_text, data_string) tuples
|
||||
"""
|
||||
Path.Log.track(enumTups)
|
||||
|
||||
# Load appropriate enumerations in each combobox
|
||||
for cb, prop in comboBoxesPropertyMap:
|
||||
box = getattr(form, cb) # Get the combobox
|
||||
box.clear() # clear the combobox
|
||||
for text, data in enumTups[prop]: # load enumerations
|
||||
box.addItem(text, data)
|
||||
|
||||
|
||||
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.
|
||||
This prevents onChanged notifications where the value didn't actually change.
|
||||
Gui::InputField and Gui::QuantitySpinBox widgets are supported - and the property can
|
||||
be of type Quantity or Float.
|
||||
If onBeforeChange is specified it is called before a new value is assigned to the property.
|
||||
Returns True if a new value was assigned, False otherwise (new value is the same as the current).
|
||||
"""
|
||||
value = widget.property("rawValue")
|
||||
Path.Log.track("value: {}".format(value))
|
||||
attr = PathUtil.getProperty(obj, prop)
|
||||
attrValue = attr.Value if hasattr(attr, "Value") else attr
|
||||
|
||||
isDiff = False
|
||||
if not Path.Geom.isRoughly(attrValue, value):
|
||||
isDiff = True
|
||||
else:
|
||||
if hasattr(obj, "ExpressionEngine"):
|
||||
exprSet = False
|
||||
for (prp, expr) in obj.ExpressionEngine:
|
||||
if prp == prop:
|
||||
exprSet = True
|
||||
Path.Log.debug('prop = "expression": {} = "{}"'.format(prp, expr))
|
||||
value = FreeCAD.Units.Quantity(obj.evalExpression(expr)).Value
|
||||
if not Path.Geom.isRoughly(attrValue, value):
|
||||
isDiff = True
|
||||
break
|
||||
if exprSet:
|
||||
widget.setReadOnly(True)
|
||||
widget.setStyleSheet("color: gray")
|
||||
else:
|
||||
widget.setReadOnly(False)
|
||||
widget.setStyleSheet("color: black")
|
||||
widget.update()
|
||||
|
||||
if isDiff:
|
||||
Path.Log.debug(
|
||||
"updateInputField(%s, %s): %.2f -> %.2f" % (obj.Label, prop, attr, value)
|
||||
)
|
||||
if onBeforeChange:
|
||||
onBeforeChange(obj)
|
||||
PathUtil.setProperty(obj, prop, value)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class QuantitySpinBox(QtCore.QObject):
|
||||
"""Controller class to interface a Gui::QuantitySpinBox.
|
||||
The spin box gets bound to a given property and supports update in both directions.
|
||||
QuatitySpinBox(widget, obj, prop, onBeforeChange=None)
|
||||
widget ... expected to be reference to a Gui::QuantitySpinBox
|
||||
obj ... document object
|
||||
prop ... canonical name of the (sub-) property
|
||||
onBeforeChange ... an optional callback being executed before the value of the property is changed
|
||||
"""
|
||||
|
||||
def __init__(self, widget, obj, prop, onBeforeChange=None):
|
||||
super().__init__()
|
||||
Path.Log.track(widget)
|
||||
self.widget = widget
|
||||
self.onBeforeChange = onBeforeChange
|
||||
self.prop = None
|
||||
self.obj = obj
|
||||
self.lastWidgetText = self.widget.text()
|
||||
self.attachTo(obj, prop)
|
||||
self.widget.installEventFilter(self)
|
||||
# Connect local class method as slot
|
||||
self.widget.textChanged.connect(self.onWidgetValueChanged)
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
if event.type() == QtCore.QEvent.Type.FocusIn:
|
||||
self.updateSpinBox()
|
||||
return False
|
||||
|
||||
def onWidgetValueChanged(self):
|
||||
"""onWidgetValueChanged()... Slot method for determining if a change
|
||||
in widget value is a result of an expression edit, or a simple spinbox change.
|
||||
If the former, emit a manual `editingFinished` signal because the Formula Editor
|
||||
window returned a value to the base widget, leaving it in read-only mode,
|
||||
and finishing the editing of the value. Otherwise, due nothing if the value
|
||||
has not changed, or there is no active expression for the property.
|
||||
If the user closes the Formula Editor to cancel the edit, the value will not
|
||||
be changed, and this manual signal will not be emitted."""
|
||||
if self._hasExpression() and self.widget.text() != self.lastWidgetText:
|
||||
self.widget.editingFinished.emit()
|
||||
|
||||
def attachTo(self, obj, prop=None):
|
||||
"""attachTo(obj, prop=None) ... use an existing editor for the given object and property"""
|
||||
Path.Log.track(self.prop, prop)
|
||||
self.obj = obj
|
||||
self.prop = prop
|
||||
if obj and prop:
|
||||
attr = PathUtil.getProperty(obj, prop)
|
||||
if attr is not None:
|
||||
if hasattr(attr, "Value"):
|
||||
self.widget.setProperty("unit", attr.getUserPreferred()[2])
|
||||
self.widget.setProperty("binding", "%s.%s" % (obj.Name, prop))
|
||||
self.valid = True
|
||||
else:
|
||||
Path.Log.warning("Cannot find property {} of {}".format(prop, obj.Label))
|
||||
self.valid = False
|
||||
else:
|
||||
self.valid = False
|
||||
|
||||
def expression(self):
|
||||
"""expression() ... returns the expression if one is bound to the property"""
|
||||
Path.Log.track(self.prop, self.valid)
|
||||
if self.valid:
|
||||
return self.widget.property("expression")
|
||||
return ""
|
||||
|
||||
def setMinimum(self, quantity):
|
||||
"""setMinimum(quantity) ... set the minimum"""
|
||||
Path.Log.track(self.prop, self.valid)
|
||||
if self.valid:
|
||||
value = quantity.Value if hasattr(quantity, "Value") else quantity
|
||||
self.widget.setProperty("setMinimum", value)
|
||||
|
||||
def updateSpinBox(self, quantity=None):
|
||||
"""updateSpinBox(quantity=None) ... update the display value of the spin box.
|
||||
If no value is provided the value of the bound property is used.
|
||||
quantity can be of type Quantity or Float."""
|
||||
Path.Log.track(self.prop, self.valid, quantity)
|
||||
|
||||
if self.valid:
|
||||
expr = self._hasExpression()
|
||||
if quantity is None:
|
||||
if expr:
|
||||
quantity = FreeCAD.Units.Quantity(self.obj.evalExpression(expr))
|
||||
else:
|
||||
quantity = PathUtil.getProperty(self.obj, self.prop)
|
||||
value = quantity.Value if hasattr(quantity, "Value") else quantity
|
||||
self.widget.setProperty("rawValue", value)
|
||||
self.lastWidgetText = self.widget.text() # update last widget value
|
||||
if expr:
|
||||
self.widget.setReadOnly(True)
|
||||
self.widget.setStyleSheet("color: gray")
|
||||
else:
|
||||
self.widget.setReadOnly(False)
|
||||
self.widget.setStyleSheet("color: black")
|
||||
|
||||
def updateProperty(self):
|
||||
"""updateProperty() ... update the bound property with the value from the spin box"""
|
||||
Path.Log.track(self.prop, self.valid)
|
||||
if self.valid:
|
||||
return updateInputField(
|
||||
self.obj, self.prop, self.widget, self.onBeforeChange
|
||||
)
|
||||
return None
|
||||
|
||||
def _hasExpression(self):
|
||||
for (prop, exp) in self.obj.ExpressionEngine:
|
||||
if prop == self.prop:
|
||||
return exp
|
||||
return None
|
||||
|
||||
|
||||
def getDocNode():
|
||||
doc = FreeCADGui.ActiveDocument.Document.Name
|
||||
tws = FreeCADGui.getMainWindow().findChildren(QtGui.QTreeWidget)
|
||||
|
||||
for tw in tws:
|
||||
if tw.topLevelItemCount() != 1 or tw.topLevelItem(0).text(0) != "Application":
|
||||
continue
|
||||
toptree = tw.topLevelItem(0)
|
||||
for i in range(0, toptree.childCount()):
|
||||
docitem = toptree.child(i)
|
||||
if docitem.text(0) == doc:
|
||||
return docitem
|
||||
return None
|
||||
|
||||
|
||||
def disableItem(item):
|
||||
Dragflag = QtCore.Qt.ItemFlag.ItemIsDragEnabled
|
||||
Dropflag = QtCore.Qt.ItemFlag.ItemIsDropEnabled
|
||||
item.setFlags(item.flags() & ~Dragflag)
|
||||
item.setFlags(item.flags() & ~Dropflag)
|
||||
for idx in range(0, item.childCount()):
|
||||
disableItem(item.child(idx))
|
||||
|
||||
|
||||
def findItem(docitem, objname):
|
||||
print(docitem.text(0))
|
||||
for i in range(0, docitem.childCount()):
|
||||
if docitem.child(i).text(0) == objname:
|
||||
return docitem.child(i)
|
||||
res = findItem(docitem.child(i), objname)
|
||||
if res:
|
||||
return res
|
||||
return None
|
||||
@@ -1,99 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2018 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 Path
|
||||
import subprocess
|
||||
|
||||
LOGLEVEL = False
|
||||
|
||||
if LOGLEVEL:
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
else:
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
|
||||
Processed = False
|
||||
|
||||
|
||||
def Startup():
|
||||
global Processed
|
||||
if not Processed:
|
||||
Path.Log.debug("Initializing PathGui")
|
||||
from Path.Op.Gui import Adaptive
|
||||
from Path.Dressup.Gui import AxisMap
|
||||
from Path.Dressup.Gui import Dogbone
|
||||
from Path.Dressup.Gui import Dragknife
|
||||
from Path.Dressup.Gui import LeadInOut
|
||||
from Path.Dressup.Gui import PathBoundary
|
||||
from Path.Dressup.Gui import RampEntry
|
||||
from Path.Dressup.Gui import Tags
|
||||
from Path.Dressup.Gui import ZCorrect
|
||||
from Path.Op.Gui import Custom
|
||||
from Path.Op.Gui import Deburr
|
||||
from Path.Op.Gui import Drilling
|
||||
from Path.Op.Gui import Engrave
|
||||
from Path.Op.Gui import Helix
|
||||
from Path.Op.Gui import MillFace
|
||||
from Path.Op.Gui import Pocket
|
||||
from Path.Op.Gui import PocketShape
|
||||
from Path.Op.Gui import Probe
|
||||
from Path.Op.Gui import Profile
|
||||
from Path.Op.Gui import Slot
|
||||
from Path.Op.Gui import ThreadMilling
|
||||
from Path.Op.Gui import Vcarve
|
||||
from Path.Post import Command
|
||||
from Path.Tool import Controller
|
||||
from Path.Tool.Gui import Controller
|
||||
from PathScripts import PathArray
|
||||
from PathScripts import PathComment
|
||||
from PathScripts import PathFixture
|
||||
from PathScripts import PathHop
|
||||
from PathScripts import PathInspect
|
||||
from PathScripts import PathPropertyBagGui
|
||||
from PathScripts import PathSanity
|
||||
from PathScripts import PathSetupSheetGui
|
||||
from PathScripts import PathSimpleCopy
|
||||
from PathScripts import PathSimulatorGui
|
||||
from PathScripts import PathStop
|
||||
from PathScripts import PathUtilsGui
|
||||
|
||||
from packaging.version import Version, parse
|
||||
|
||||
# If camotics is installed and current enough, import the GUI
|
||||
try:
|
||||
import camotics
|
||||
|
||||
r = subprocess.run(
|
||||
["camotics", "--version"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
).stderr.strip()
|
||||
|
||||
v = parse(r.decode("utf-8"))
|
||||
if v >= Version("1.2.2"):
|
||||
from PathScripts import PathCamoticsGui
|
||||
except (FileNotFoundError, ModuleNotFoundError):
|
||||
pass
|
||||
|
||||
Processed = True
|
||||
else:
|
||||
Path.Log.debug("Skipping PathGui initialisation")
|
||||
@@ -1,125 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2017 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 Path
|
||||
import Path.Base.Util as PathUtil
|
||||
import PathGui
|
||||
import importlib
|
||||
|
||||
__title__ = "Path Icon ViewProvider"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "ViewProvider who's main and only task is to assign an icon."
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
if False:
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
else:
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
|
||||
|
||||
class ViewProvider(object):
|
||||
"""Generic view provider to assign an icon."""
|
||||
|
||||
def __init__(self, vobj, icon):
|
||||
self.icon = icon
|
||||
self.attach(vobj)
|
||||
|
||||
self.editModule = None
|
||||
self.editCallback = None
|
||||
|
||||
vobj.Proxy = self
|
||||
|
||||
def attach(self, vobj):
|
||||
self.vobj = vobj
|
||||
self.obj = vobj.Object
|
||||
|
||||
def __getstate__(self):
|
||||
attrs = {"icon": self.icon}
|
||||
if hasattr(self, "editModule"):
|
||||
attrs["editModule"] = self.editModule
|
||||
attrs["editCallback"] = self.editCallback
|
||||
return attrs
|
||||
|
||||
def __setstate__(self, state):
|
||||
self.icon = state["icon"]
|
||||
if state.get("editModule", None):
|
||||
self.editModule = state["editModule"]
|
||||
self.editCallback = state["editCallback"]
|
||||
|
||||
def getIcon(self):
|
||||
return ":/icons/Path_{}.svg".format(self.icon)
|
||||
|
||||
def onEdit(self, callback):
|
||||
self.editModule = callback.__module__
|
||||
self.editCallback = callback.__name__
|
||||
|
||||
def _onEditCallback(self, edit):
|
||||
if hasattr(self, "editModule"):
|
||||
mod = importlib.import_module(self.editModule)
|
||||
callback = getattr(mod, self.editCallback)
|
||||
callback(self.obj, self.vobj, edit)
|
||||
|
||||
def setEdit(self, vobj=None, mode=0):
|
||||
if 0 == mode:
|
||||
self._onEditCallback(True)
|
||||
return False
|
||||
|
||||
def unsetEdit(self, arg1, arg2):
|
||||
self._onEditCallback(False)
|
||||
|
||||
def setupContextMenu(self, vobj, menu):
|
||||
Path.Log.track()
|
||||
from PySide import QtGui
|
||||
|
||||
edit = translate("Path", "Edit")
|
||||
action = QtGui.QAction(edit, menu)
|
||||
action.triggered.connect(self.setEdit)
|
||||
menu.addAction(action)
|
||||
|
||||
|
||||
_factory = {}
|
||||
|
||||
|
||||
def Attach(vobj, name):
|
||||
"""Attach(vobj, name) ... attach the appropriate view provider to the view object.
|
||||
If no view provider was registered for the given name a default IconViewProvider is created."""
|
||||
|
||||
Path.Log.track(vobj.Object.Label, name)
|
||||
global _factory
|
||||
for key, value in PathUtil.keyValueIter(_factory):
|
||||
if key == name:
|
||||
return value(vobj, name)
|
||||
Path.Log.track(vobj.Object.Label, name, "PathIconViewProvider")
|
||||
return ViewProvider(vobj, name)
|
||||
|
||||
|
||||
def RegisterViewProvider(name, provider):
|
||||
"""RegisterViewProvider(name, provider) ... if an IconViewProvider is created for an object with the given name
|
||||
an instance of provider is used instead."""
|
||||
|
||||
Path.Log.track(name)
|
||||
global _factory
|
||||
_factory[name] = provider
|
||||
@@ -24,11 +24,10 @@ from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
import FreeCAD
|
||||
import Path
|
||||
import Path.Base.SetupSheet as PathSetupSheet
|
||||
import Path.Base.Util as PathUtil
|
||||
from Path.Post.Processor import PostProcessor
|
||||
import Path.Tool.Controller as PathToolController
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathSetupSheet as PathSetupSheet
|
||||
import PathScripts.PathStock as PathStock
|
||||
import json
|
||||
import time
|
||||
@@ -83,9 +82,9 @@ def createResourceClone(obj, orig, name, icon):
|
||||
clone.addProperty("App::PropertyString", "PathResource")
|
||||
clone.PathResource = name
|
||||
if clone.ViewObject:
|
||||
import PathScripts.PathIconViewProvider
|
||||
import Path.Base.Gui.IconViewProvider
|
||||
|
||||
PathScripts.PathIconViewProvider.Attach(clone.ViewObject, icon)
|
||||
Path.Base.Gui.IconViewProvider.Attach(clone.ViewObject, icon)
|
||||
clone.ViewObject.Visibility = False
|
||||
clone.ViewObject.Transparency = 80
|
||||
obj.Document.recompute() # necessary to create the clone shape
|
||||
@@ -223,16 +222,16 @@ class ObjectJob:
|
||||
for n in self.propertyEnumerations():
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
obj.PostProcessorOutputFile = PathPreferences.defaultOutputFile()
|
||||
obj.PostProcessor = postProcessors = PathPreferences.allEnabledPostProcessors()
|
||||
defaultPostProcessor = PathPreferences.defaultPostProcessor()
|
||||
obj.PostProcessorOutputFile = Path.Preferences.defaultOutputFile()
|
||||
obj.PostProcessor = postProcessors = Path.Preferences.allEnabledPostProcessors()
|
||||
defaultPostProcessor = Path.Preferences.defaultPostProcessor()
|
||||
# Check to see if default post processor hasn't been 'lost' (This can happen when Macro dir has changed)
|
||||
if defaultPostProcessor in postProcessors:
|
||||
obj.PostProcessor = defaultPostProcessor
|
||||
else:
|
||||
obj.PostProcessor = postProcessors[0]
|
||||
obj.PostProcessorArgs = PathPreferences.defaultPostProcessorArgs()
|
||||
obj.GeometryTolerance = PathPreferences.defaultGeometryTolerance()
|
||||
obj.PostProcessorArgs = Path.Preferences.defaultPostProcessorArgs()
|
||||
obj.GeometryTolerance = Path.Preferences.defaultGeometryTolerance()
|
||||
|
||||
self.setupOperations(obj)
|
||||
self.setupSetupSheet(obj)
|
||||
@@ -307,9 +306,9 @@ class ObjectJob:
|
||||
)
|
||||
obj.SetupSheet = PathSetupSheet.Create()
|
||||
if obj.SetupSheet.ViewObject:
|
||||
import PathScripts.PathIconViewProvider
|
||||
import Path.Base.Gui.IconViewProvider
|
||||
|
||||
PathScripts.PathIconViewProvider.Attach(
|
||||
Path.Base.Gui.IconViewProvider.Attach(
|
||||
obj.SetupSheet.ViewObject, "SetupSheet"
|
||||
)
|
||||
obj.SetupSheet.Label = "SetupSheet"
|
||||
@@ -383,7 +382,7 @@ class ObjectJob:
|
||||
def setupStock(self, obj):
|
||||
"""setupStock(obj)... setup the Stock for the Job object."""
|
||||
if not obj.Stock:
|
||||
stockTemplate = PathPreferences.defaultStockTemplate()
|
||||
stockTemplate = Path.Preferences.defaultStockTemplate()
|
||||
if stockTemplate:
|
||||
obj.Stock = PathStock.CreateFromTemplate(obj, json.loads(stockTemplate))
|
||||
if not obj.Stock:
|
||||
|
||||
@@ -28,7 +28,6 @@ import Path
|
||||
import Path.Base.Util as PathUtil
|
||||
import PathScripts.PathJob as PathJob
|
||||
import PathScripts.PathJobDlg as PathJobDlg
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathStock as PathStock
|
||||
import json
|
||||
import os
|
||||
@@ -136,7 +135,7 @@ class CommandJobTemplateExport:
|
||||
foo = QtGui.QFileDialog.getSaveFileName(
|
||||
QtGui.QApplication.activeWindow(),
|
||||
"Path - Job Template",
|
||||
PathPreferences.filePath(),
|
||||
Path.Preferences.filePath(),
|
||||
"job_*.json",
|
||||
)[0]
|
||||
if foo:
|
||||
|
||||
@@ -27,7 +27,6 @@ import FreeCADGui
|
||||
import Path
|
||||
import Path.Base.Util as PathUtil
|
||||
import PathScripts.PathJob as PathJob
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathStock as PathStock
|
||||
import glob
|
||||
import os
|
||||
@@ -223,7 +222,7 @@ class JobCreate:
|
||||
|
||||
def setupTemplate(self):
|
||||
templateFiles = []
|
||||
for path in PathPreferences.searchPaths():
|
||||
for path in Path.Preferences.searchPaths():
|
||||
cleanPaths = [
|
||||
f.replace("\\", "/") for f in self.templateFilesIn(path)
|
||||
] # Standardize slashes used across os platforms
|
||||
@@ -240,7 +239,7 @@ class JobCreate:
|
||||
name = basename + " (%s)" % i
|
||||
Path.Log.track(name, tFile)
|
||||
template[name] = tFile
|
||||
selectTemplate = PathPreferences.defaultJobTemplate()
|
||||
selectTemplate = Path.Preferences.defaultJobTemplate()
|
||||
index = 0
|
||||
self.dialog.jobTemplate.addItem("<none>", "")
|
||||
for name in sorted(template.keys()):
|
||||
|
||||
@@ -28,15 +28,14 @@ from pivy import coin
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Path
|
||||
import Path.Base.Gui.SetupSheetGui as PathSetupSheetGui
|
||||
import Path.Base.Util as PathUtil
|
||||
import Path.GuiInit as PathGuiInit
|
||||
import Path.Tool.Gui.Bit as PathToolBitGui
|
||||
import Path.Tool.Gui.Controller as PathToolControllerGui
|
||||
import PathScripts.PathGuiInit as PathGuiInit
|
||||
import PathScripts.PathJob as PathJob
|
||||
import PathScripts.PathJobCmd as PathJobCmd
|
||||
import PathScripts.PathJobDlg as PathJobDlg
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathSetupSheetGui as PathSetupSheetGui
|
||||
import PathScripts.PathStock as PathStock
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import json
|
||||
@@ -635,7 +634,7 @@ class TaskPanel:
|
||||
self.form.toolControllerList.resizeColumnsToContents()
|
||||
|
||||
currentPostProcessor = self.obj.PostProcessor
|
||||
postProcessors = PathPreferences.allEnabledPostProcessors(
|
||||
postProcessors = Path.Preferences.allEnabledPostProcessors(
|
||||
["", currentPostProcessor]
|
||||
)
|
||||
for post in postProcessors:
|
||||
@@ -999,7 +998,7 @@ class TaskPanel:
|
||||
|
||||
tools = PathToolBitGui.LoadTools()
|
||||
|
||||
curLib = PathPreferences.lastFileToolLibrary()
|
||||
curLib = Path.Preferences.lastFileToolLibrary()
|
||||
|
||||
library = None
|
||||
if curLib is not None:
|
||||
|
||||
@@ -1,356 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
|
||||
# * *
|
||||
# * 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 Path
|
||||
import glob
|
||||
import os
|
||||
from PySide.QtGui import QMessageBox
|
||||
|
||||
if False:
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
else:
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
DefaultFilePath = "DefaultFilePath"
|
||||
DefaultJobTemplate = "DefaultJobTemplate"
|
||||
DefaultStockTemplate = "DefaultStockTemplate"
|
||||
DefaultTaskPanelLayout = "DefaultTaskPanelLayout"
|
||||
|
||||
PostProcessorDefault = "PostProcessorDefault"
|
||||
PostProcessorDefaultArgs = "PostProcessorDefaultArgs"
|
||||
PostProcessorBlacklist = "PostProcessorBlacklist"
|
||||
PostProcessorOutputFile = "PostProcessorOutputFile"
|
||||
PostProcessorOutputPolicy = "PostProcessorOutputPolicy"
|
||||
|
||||
LastPathToolBit = "LastPathToolBit"
|
||||
LastPathToolLibrary = "LastPathToolLibrary"
|
||||
LastPathToolShape = "LastPathToolShape"
|
||||
LastPathToolTable = "LastPathToolTable"
|
||||
|
||||
LastFileToolBit = "LastFileToolBit"
|
||||
LastFileToolLibrary = "LastFileToolLibrary"
|
||||
LastFileToolShape = "LastFileToolShape"
|
||||
|
||||
UseAbsoluteToolPaths = "UseAbsoluteToolPaths"
|
||||
# OpenLastLibrary = "OpenLastLibrary"
|
||||
|
||||
# Linear tolerance to use when generating Paths, eg when tessellating geometry
|
||||
GeometryTolerance = "GeometryTolerance"
|
||||
LibAreaCurveAccuracy = "LibAreaCurveAccuracy"
|
||||
|
||||
WarningSuppressRapidSpeeds = "WarningSuppressRapidSpeeds"
|
||||
WarningSuppressAllSpeeds = "WarningSuppressAllSpeeds"
|
||||
WarningSuppressSelectionMode = "WarningSuppressSelectionMode"
|
||||
WarningSuppressOpenCamLib = "WarningSuppressOpenCamLib"
|
||||
WarningSuppressVelocity = "WarningSuppressVelocity"
|
||||
EnableExperimentalFeatures = "EnableExperimentalFeatures"
|
||||
EnableAdvancedOCLFeatures = "EnableAdvancedOCLFeatures"
|
||||
|
||||
|
||||
def preferences():
|
||||
return FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Path")
|
||||
|
||||
|
||||
def pathPostSourcePath():
|
||||
return os.path.join(FreeCAD.getHomePath(), "Mod/Path/Path/Post/")
|
||||
|
||||
|
||||
def pathDefaultToolsPath(sub=None):
|
||||
if sub:
|
||||
return os.path.join(FreeCAD.getHomePath(), "Mod/Path/Tools/", sub)
|
||||
return os.path.join(FreeCAD.getHomePath(), "Mod/Path/Tools/")
|
||||
|
||||
|
||||
def allAvailablePostProcessors():
|
||||
allposts = []
|
||||
for path in searchPathsPost():
|
||||
posts = [
|
||||
str(os.path.split(os.path.splitext(p)[0])[1][:-5])
|
||||
for p in glob.glob(path + "/*_post.py")
|
||||
]
|
||||
allposts.extend(posts)
|
||||
allposts.sort()
|
||||
return allposts
|
||||
|
||||
|
||||
def allEnabledPostProcessors(include=None):
|
||||
blacklist = postProcessorBlacklist()
|
||||
enabled = [
|
||||
processor
|
||||
for processor in allAvailablePostProcessors()
|
||||
if processor not in blacklist
|
||||
]
|
||||
if include:
|
||||
postlist = list(set(include + enabled))
|
||||
postlist.sort()
|
||||
return postlist
|
||||
return enabled
|
||||
|
||||
|
||||
def defaultPostProcessor():
|
||||
pref = preferences()
|
||||
return pref.GetString(PostProcessorDefault, "")
|
||||
|
||||
|
||||
def defaultPostProcessorArgs():
|
||||
pref = preferences()
|
||||
return pref.GetString(PostProcessorDefaultArgs, "")
|
||||
|
||||
|
||||
def defaultGeometryTolerance():
|
||||
return preferences().GetFloat(GeometryTolerance, 0.01)
|
||||
|
||||
|
||||
def defaultLibAreaCurveAccuracy():
|
||||
return preferences().GetFloat(LibAreaCurveAccuracy, 0.01)
|
||||
|
||||
|
||||
def defaultFilePath():
|
||||
return preferences().GetString(DefaultFilePath)
|
||||
|
||||
|
||||
def filePath():
|
||||
path = defaultFilePath()
|
||||
if not path:
|
||||
path = macroFilePath()
|
||||
return path
|
||||
|
||||
|
||||
def macroFilePath():
|
||||
grp = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")
|
||||
return grp.GetString("MacroPath", FreeCAD.getUserMacroDir())
|
||||
|
||||
|
||||
def searchPaths():
|
||||
paths = []
|
||||
p = defaultFilePath()
|
||||
if p:
|
||||
paths.append(p)
|
||||
paths.append(macroFilePath())
|
||||
return paths
|
||||
|
||||
|
||||
def searchPathsPost():
|
||||
paths = []
|
||||
p = defaultFilePath()
|
||||
if p:
|
||||
paths.append(p)
|
||||
paths.append(macroFilePath())
|
||||
paths.append(os.path.join(pathPostSourcePath(), "scripts/"))
|
||||
paths.append(pathPostSourcePath())
|
||||
return paths
|
||||
|
||||
|
||||
def searchPathsTool(sub):
|
||||
paths = []
|
||||
paths.append(os.path.join(FreeCAD.getHomePath(), "Mod", "Path", "Tools", sub))
|
||||
return paths
|
||||
|
||||
|
||||
|
||||
def toolsStoreAbsolutePaths():
|
||||
return preferences().GetBool(UseAbsoluteToolPaths, False)
|
||||
|
||||
|
||||
# def toolsOpenLastLibrary():
|
||||
# return preferences().GetBool(OpenLastLibrary, False)
|
||||
|
||||
|
||||
def setToolsSettings(relative):
|
||||
pref = preferences()
|
||||
pref.SetBool(UseAbsoluteToolPaths, relative)
|
||||
# pref.SetBool(OpenLastLibrary, lastlibrary)
|
||||
|
||||
|
||||
def defaultJobTemplate():
|
||||
template = preferences().GetString(DefaultJobTemplate)
|
||||
if "xml" not in template:
|
||||
return template
|
||||
return ""
|
||||
|
||||
|
||||
def setJobDefaults(fileName, jobTemplate, geometryTolerance, curveAccuracy):
|
||||
Path.Log.track(
|
||||
"(%s='%s', %s, %s, %s)"
|
||||
% (DefaultFilePath, fileName, jobTemplate, geometryTolerance, curveAccuracy)
|
||||
)
|
||||
pref = preferences()
|
||||
pref.SetString(DefaultFilePath, fileName)
|
||||
pref.SetString(DefaultJobTemplate, jobTemplate)
|
||||
pref.SetFloat(GeometryTolerance, geometryTolerance)
|
||||
pref.SetFloat(LibAreaCurveAccuracy, curveAccuracy)
|
||||
|
||||
|
||||
def postProcessorBlacklist():
|
||||
pref = preferences()
|
||||
blacklist = pref.GetString(PostProcessorBlacklist, "")
|
||||
if not blacklist:
|
||||
return []
|
||||
return eval(blacklist)
|
||||
|
||||
|
||||
def setPostProcessorDefaults(processor, args, blacklist):
|
||||
pref = preferences()
|
||||
pref.SetString(PostProcessorDefault, processor)
|
||||
pref.SetString(PostProcessorDefaultArgs, args)
|
||||
pref.SetString(PostProcessorBlacklist, "%s" % (blacklist))
|
||||
|
||||
|
||||
def setOutputFileDefaults(fileName, policy):
|
||||
pref = preferences()
|
||||
pref.SetString(PostProcessorOutputFile, fileName)
|
||||
pref.SetString(PostProcessorOutputPolicy, policy)
|
||||
|
||||
|
||||
def defaultOutputFile():
|
||||
pref = preferences()
|
||||
return pref.GetString(PostProcessorOutputFile, "")
|
||||
|
||||
|
||||
def defaultOutputPolicy():
|
||||
pref = preferences()
|
||||
return pref.GetString(PostProcessorOutputPolicy, "")
|
||||
|
||||
|
||||
def defaultStockTemplate():
|
||||
return preferences().GetString(DefaultStockTemplate, "")
|
||||
|
||||
|
||||
def setDefaultStockTemplate(template):
|
||||
preferences().SetString(DefaultStockTemplate, template)
|
||||
|
||||
|
||||
def defaultTaskPanelLayout():
|
||||
return preferences().GetInt(DefaultTaskPanelLayout, 0)
|
||||
|
||||
|
||||
def setDefaultTaskPanelLayout(style):
|
||||
preferences().SetInt(DefaultTaskPanelLayout, style)
|
||||
|
||||
|
||||
def advancedOCLFeaturesEnabled():
|
||||
return preferences().GetBool(EnableAdvancedOCLFeatures, False)
|
||||
|
||||
|
||||
def experimentalFeaturesEnabled():
|
||||
return preferences().GetBool(EnableExperimentalFeatures, False)
|
||||
|
||||
|
||||
def suppressAllSpeedsWarning():
|
||||
return preferences().GetBool(WarningSuppressAllSpeeds, True)
|
||||
|
||||
|
||||
def suppressRapidSpeedsWarning(user=True):
|
||||
return (user and suppressAllSpeedsWarning()) or preferences().GetBool(
|
||||
WarningSuppressRapidSpeeds, True
|
||||
)
|
||||
|
||||
|
||||
def suppressSelectionModeWarning():
|
||||
return preferences().GetBool(WarningSuppressSelectionMode, True)
|
||||
|
||||
|
||||
def suppressOpenCamLibWarning():
|
||||
return preferences().GetBool(WarningSuppressOpenCamLib, True)
|
||||
|
||||
|
||||
def suppressVelocity():
|
||||
return preferences().GetBool(WarningSuppressVelocity, False)
|
||||
|
||||
|
||||
def setPreferencesAdvanced(
|
||||
ocl, warnSpeeds, warnRapids, warnModes, warnOCL, warnVelocity
|
||||
):
|
||||
preferences().SetBool(EnableAdvancedOCLFeatures, ocl)
|
||||
preferences().SetBool(WarningSuppressAllSpeeds, warnSpeeds)
|
||||
preferences().SetBool(WarningSuppressRapidSpeeds, warnRapids)
|
||||
preferences().SetBool(WarningSuppressSelectionMode, warnModes)
|
||||
preferences().SetBool(WarningSuppressOpenCamLib, warnOCL)
|
||||
preferences().SetBool(WarningSuppressVelocity, warnVelocity)
|
||||
|
||||
|
||||
def lastFileToolLibrary():
|
||||
filename = preferences().GetString(LastFileToolLibrary)
|
||||
if filename.endswith(".fctl") and os.path.isfile(filename):
|
||||
return filename
|
||||
|
||||
libpath = preferences().GetString(
|
||||
LastPathToolLibrary, pathDefaultToolsPath("Library")
|
||||
)
|
||||
libFiles = [f for f in glob.glob(libpath + "/*.fctl")]
|
||||
libFiles.sort()
|
||||
if len(libFiles) >= 1:
|
||||
filename = libFiles[0]
|
||||
setLastFileToolLibrary(filename)
|
||||
Path.Log.track(filename)
|
||||
return filename
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def setLastFileToolLibrary(path):
|
||||
Path.Log.track(path)
|
||||
if os.path.isfile(path): # keep the path and file in sync
|
||||
preferences().SetString(LastPathToolLibrary, os.path.split(path)[0])
|
||||
return preferences().SetString(LastFileToolLibrary, path)
|
||||
|
||||
|
||||
def lastPathToolBit():
|
||||
return preferences().GetString(LastPathToolBit, pathDefaultToolsPath("Bit"))
|
||||
|
||||
|
||||
def setLastPathToolBit(path):
|
||||
return preferences().SetString(LastPathToolBit, path)
|
||||
|
||||
|
||||
def lastPathToolLibrary():
|
||||
Path.Log.track()
|
||||
return preferences().GetString(LastPathToolLibrary, pathDefaultToolsPath("Library"))
|
||||
|
||||
|
||||
def setLastPathToolLibrary(path):
|
||||
Path.Log.track(path)
|
||||
curLib = lastFileToolLibrary()
|
||||
Path.Log.debug("curLib: {}".format(curLib))
|
||||
if curLib and os.path.split(curLib)[0] != path:
|
||||
setLastFileToolLibrary("") # a path is known but not specific file
|
||||
return preferences().SetString(LastPathToolLibrary, path)
|
||||
|
||||
|
||||
def lastPathToolShape():
|
||||
return preferences().GetString(LastPathToolShape, pathDefaultToolsPath("Shape"))
|
||||
|
||||
|
||||
def setLastPathToolShape(path):
|
||||
return preferences().SetString(LastPathToolShape, path)
|
||||
|
||||
|
||||
def lastPathToolTable():
|
||||
return preferences().GetString(LastPathToolTable, "")
|
||||
|
||||
|
||||
def setLastPathToolTable(table):
|
||||
return preferences().SetString(LastPathToolTable, table)
|
||||
@@ -1,79 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2021 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 FreeCADGui
|
||||
import Path
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
|
||||
if False:
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
else:
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
|
||||
|
||||
class AdvancedPreferencesPage:
|
||||
def __init__(self, parent=None):
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":preferences/Advanced.ui")
|
||||
self.form.WarningSuppressAllSpeeds.stateChanged.connect(self.updateSelection)
|
||||
self.form.EnableAdvancedOCLFeatures.stateChanged.connect(self.updateSelection)
|
||||
|
||||
def saveSettings(self):
|
||||
PathPreferences.setPreferencesAdvanced(
|
||||
self.form.EnableAdvancedOCLFeatures.isChecked(),
|
||||
self.form.WarningSuppressAllSpeeds.isChecked(),
|
||||
self.form.WarningSuppressRapidSpeeds.isChecked(),
|
||||
self.form.WarningSuppressSelectionMode.isChecked(),
|
||||
self.form.WarningSuppressOpenCamLib.isChecked(),
|
||||
self.form.WarningSuppressVelocity.isChecked(),
|
||||
)
|
||||
|
||||
def loadSettings(self):
|
||||
Path.Log.track()
|
||||
self.form.WarningSuppressAllSpeeds.setChecked(
|
||||
PathPreferences.suppressAllSpeedsWarning()
|
||||
)
|
||||
self.form.WarningSuppressRapidSpeeds.setChecked(
|
||||
PathPreferences.suppressRapidSpeedsWarning(False)
|
||||
)
|
||||
self.form.WarningSuppressSelectionMode.setChecked(
|
||||
PathPreferences.suppressSelectionModeWarning()
|
||||
)
|
||||
self.form.EnableAdvancedOCLFeatures.setChecked(
|
||||
PathPreferences.advancedOCLFeaturesEnabled()
|
||||
)
|
||||
self.form.WarningSuppressOpenCamLib.setChecked(
|
||||
PathPreferences.suppressOpenCamLibWarning()
|
||||
)
|
||||
self.form.WarningSuppressVelocity.setChecked(PathPreferences.suppressVelocity())
|
||||
self.updateSelection()
|
||||
|
||||
def updateSelection(self, state=None):
|
||||
self.form.WarningSuppressOpenCamLib.setEnabled(
|
||||
self.form.EnableAdvancedOCLFeatures.isChecked()
|
||||
)
|
||||
|
||||
if self.form.WarningSuppressAllSpeeds.isChecked():
|
||||
self.form.WarningSuppressRapidSpeeds.setChecked(True)
|
||||
self.form.WarningSuppressRapidSpeeds.setEnabled(False)
|
||||
else:
|
||||
self.form.WarningSuppressRapidSpeeds.setEnabled(True)
|
||||
@@ -23,7 +23,6 @@
|
||||
import FreeCAD
|
||||
import Path
|
||||
import Path.Post.Processor as PostProcessor
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathStock as PathStock
|
||||
import json
|
||||
|
||||
@@ -52,7 +51,7 @@ class JobPreferencesPage:
|
||||
jobTemplate = self.form.leDefaultJobTemplate.text()
|
||||
geometryTolerance = Units.Quantity(self.form.geometryTolerance.text())
|
||||
curveAccuracy = Units.Quantity(self.form.curveAccuracy.text())
|
||||
PathPreferences.setJobDefaults(
|
||||
Path.Preferences.setJobDefaults(
|
||||
filePath, jobTemplate, geometryTolerance, curveAccuracy
|
||||
)
|
||||
|
||||
@@ -66,11 +65,11 @@ class JobPreferencesPage:
|
||||
item = self.form.postProcessorList.item(i)
|
||||
if item.checkState() == QtCore.Qt.CheckState.Unchecked:
|
||||
blacklist.append(item.text())
|
||||
PathPreferences.setPostProcessorDefaults(processor, args, blacklist)
|
||||
Path.Preferences.setPostProcessorDefaults(processor, args, blacklist)
|
||||
|
||||
path = str(self.form.leOutputFile.text())
|
||||
policy = str(self.form.cboOutputPolicy.currentText())
|
||||
PathPreferences.setOutputFileDefaults(path, policy)
|
||||
Path.Preferences.setOutputFileDefaults(path, policy)
|
||||
self.saveStockSettings()
|
||||
self.saveToolsSettings()
|
||||
|
||||
@@ -141,12 +140,12 @@ class JobPreferencesPage:
|
||||
attrs["posZ"] = FreeCAD.Units.Quantity(
|
||||
self.form.stockPositionZ.text()
|
||||
).Value
|
||||
PathPreferences.setDefaultStockTemplate(json.dumps(attrs))
|
||||
Path.Preferences.setDefaultStockTemplate(json.dumps(attrs))
|
||||
else:
|
||||
PathPreferences.setDefaultStockTemplate("")
|
||||
Path.Preferences.setDefaultStockTemplate("")
|
||||
|
||||
def saveToolsSettings(self):
|
||||
PathPreferences.setToolsSettings(
|
||||
Path.Preferences.setToolsSettings(
|
||||
self.form.toolsAbsolutePaths.isChecked()
|
||||
)
|
||||
|
||||
@@ -181,11 +180,11 @@ class JobPreferencesPage:
|
||||
)
|
||||
|
||||
def loadSettings(self):
|
||||
self.form.leDefaultFilePath.setText(PathPreferences.defaultFilePath())
|
||||
self.form.leDefaultJobTemplate.setText(PathPreferences.defaultJobTemplate())
|
||||
self.form.leDefaultFilePath.setText(Path.Preferences.defaultFilePath())
|
||||
self.form.leDefaultJobTemplate.setText(Path.Preferences.defaultJobTemplate())
|
||||
|
||||
blacklist = PathPreferences.postProcessorBlacklist()
|
||||
for processor in PathPreferences.allAvailablePostProcessors():
|
||||
blacklist = Path.Preferences.postProcessorBlacklist()
|
||||
for processor in Path.Preferences.allAvailablePostProcessors():
|
||||
item = QtGui.QListWidgetItem(processor)
|
||||
if processor in blacklist:
|
||||
item.setCheckState(QtCore.Qt.CheckState.Unchecked)
|
||||
@@ -198,26 +197,26 @@ class JobPreferencesPage:
|
||||
)
|
||||
self.form.postProcessorList.addItem(item)
|
||||
self.verifyAndUpdateDefaultPostProcessorWith(
|
||||
PathPreferences.defaultPostProcessor()
|
||||
Path.Preferences.defaultPostProcessor()
|
||||
)
|
||||
|
||||
self.form.defaultPostProcessorArgs.setText(
|
||||
PathPreferences.defaultPostProcessorArgs()
|
||||
Path.Preferences.defaultPostProcessorArgs()
|
||||
)
|
||||
|
||||
geomTol = Units.Quantity(
|
||||
PathPreferences.defaultGeometryTolerance(), Units.Length
|
||||
Path.Preferences.defaultGeometryTolerance(), Units.Length
|
||||
)
|
||||
self.form.geometryTolerance.setText(geomTol.UserString)
|
||||
self.form.curveAccuracy.setText(
|
||||
Units.Quantity(
|
||||
PathPreferences.defaultLibAreaCurveAccuracy(), Units.Length
|
||||
Path.Preferences.defaultLibAreaCurveAccuracy(), Units.Length
|
||||
).UserString
|
||||
)
|
||||
|
||||
self.form.leOutputFile.setText(PathPreferences.defaultOutputFile())
|
||||
self.form.leOutputFile.setText(Path.Preferences.defaultOutputFile())
|
||||
self.selectComboEntry(
|
||||
self.form.cboOutputPolicy, PathPreferences.defaultOutputPolicy()
|
||||
self.form.cboOutputPolicy, Path.Preferences.defaultOutputPolicy()
|
||||
)
|
||||
|
||||
self.form.tbDefaultFilePath.clicked.connect(self.browseDefaultFilePath)
|
||||
@@ -235,7 +234,7 @@ class JobPreferencesPage:
|
||||
self.loadToolSettings()
|
||||
|
||||
def loadStockSettings(self):
|
||||
stock = PathPreferences.defaultStockTemplate()
|
||||
stock = Path.Preferences.defaultStockTemplate()
|
||||
index = -1
|
||||
if stock:
|
||||
attrs = json.loads(stock)
|
||||
@@ -328,7 +327,7 @@ class JobPreferencesPage:
|
||||
|
||||
def loadToolSettings(self):
|
||||
self.form.toolsAbsolutePaths.setChecked(
|
||||
PathPreferences.toolsStoreAbsolutePaths()
|
||||
Path.Preferences.toolsStoreAbsolutePaths()
|
||||
)
|
||||
|
||||
def getPostProcessor(self, name):
|
||||
@@ -370,7 +369,7 @@ class JobPreferencesPage:
|
||||
def bestGuessForFilePath(self):
|
||||
path = self.form.leDefaultFilePath.text()
|
||||
if not path:
|
||||
path = PathPreferences.filePath()
|
||||
path = Path.Preferences.filePath()
|
||||
return path
|
||||
|
||||
def browseDefaultJobTemplate(self):
|
||||
|
||||
@@ -1,463 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2020 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 *
|
||||
# * *
|
||||
# ***************************************************************************
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Path
|
||||
import Path.Base.PropertyBag as PathPropertyBag
|
||||
import Path.Base.Util as PathUtil
|
||||
import PathScripts.PathIconViewProvider as PathIconViewProvider
|
||||
import PathScripts.PathPropertyEditor as PathPropertyEditor
|
||||
import re
|
||||
|
||||
|
||||
__title__ = "Property Bag Editor"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Task panel editor for a PropertyBag"
|
||||
|
||||
if False:
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
else:
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
class ViewProvider(object):
|
||||
"""ViewProvider for a PropertyBag.
|
||||
It's sole job is to provide an icon and invoke the TaskPanel on edit."""
|
||||
|
||||
def __init__(self, vobj, name):
|
||||
Path.Log.track(name)
|
||||
vobj.Proxy = self
|
||||
self.icon = name
|
||||
# mode = 2
|
||||
self.obj = None
|
||||
self.vobj = None
|
||||
|
||||
def attach(self, vobj):
|
||||
Path.Log.track()
|
||||
self.vobj = vobj
|
||||
self.obj = vobj.Object
|
||||
|
||||
def getIcon(self):
|
||||
return ":/icons/Path-SetupSheet.svg"
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
return None
|
||||
|
||||
def getDisplayMode(self, mode):
|
||||
return "Default"
|
||||
|
||||
def setEdit(self, vobj, mode=0):
|
||||
Path.Log.track()
|
||||
taskPanel = TaskPanel(vobj)
|
||||
FreeCADGui.Control.closeDialog()
|
||||
FreeCADGui.Control.showDialog(taskPanel)
|
||||
taskPanel.setupUi()
|
||||
return True
|
||||
|
||||
def unsetEdit(self, vobj, mode):
|
||||
FreeCADGui.Control.closeDialog()
|
||||
return
|
||||
|
||||
def claimChildren(self):
|
||||
return []
|
||||
|
||||
def doubleClicked(self, vobj):
|
||||
self.setEdit(vobj)
|
||||
|
||||
|
||||
class Delegate(QtGui.QStyledItemDelegate):
|
||||
RoleObject = QtCore.Qt.UserRole + 1
|
||||
RoleProperty = QtCore.Qt.UserRole + 2
|
||||
RoleEditor = QtCore.Qt.UserRole + 3
|
||||
|
||||
# def paint(self, painter, option, index):
|
||||
# #Path.Log.track(index.column(), type(option))
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
editor = PathPropertyEditor.Editor(
|
||||
index.data(self.RoleObject), index.data(self.RoleProperty)
|
||||
)
|
||||
index.model().setData(index, editor, self.RoleEditor)
|
||||
return editor.widget(parent)
|
||||
|
||||
def setEditorData(self, widget, index):
|
||||
Path.Log.track(index.row(), index.column())
|
||||
index.data(self.RoleEditor).setEditorData(widget)
|
||||
|
||||
def setModelData(self, widget, model, index):
|
||||
Path.Log.track(index.row(), index.column())
|
||||
editor = index.data(self.RoleEditor)
|
||||
editor.setModelData(widget)
|
||||
index.model().setData(index, editor.displayString(), QtCore.Qt.DisplayRole)
|
||||
|
||||
def updateEditorGeometry(self, widget, option, index):
|
||||
widget.setGeometry(option.rect)
|
||||
|
||||
|
||||
class PropertyCreate(object):
|
||||
def __init__(self, obj, grp, typ, another):
|
||||
self.obj = obj
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":panels/PropertyCreate.ui")
|
||||
|
||||
obj.Proxy.refreshCustomPropertyGroups()
|
||||
for g in sorted(obj.CustomPropertyGroups):
|
||||
self.form.propertyGroup.addItem(g)
|
||||
if grp:
|
||||
self.form.propertyGroup.setCurrentText(grp)
|
||||
|
||||
for t in sorted(PathPropertyBag.SupportedPropertyType):
|
||||
self.form.propertyType.addItem(t)
|
||||
if PathPropertyBag.SupportedPropertyType[t] == typ:
|
||||
typ = t
|
||||
if typ:
|
||||
self.form.propertyType.setCurrentText(typ)
|
||||
else:
|
||||
self.form.propertyType.setCurrentText("String")
|
||||
self.form.createAnother.setChecked(another)
|
||||
|
||||
self.form.propertyGroup.currentTextChanged.connect(self.updateUI)
|
||||
self.form.propertyGroup.currentIndexChanged.connect(self.updateUI)
|
||||
self.form.propertyName.textChanged.connect(self.updateUI)
|
||||
self.form.propertyType.currentIndexChanged.connect(self.updateUI)
|
||||
self.form.propertyEnum.textChanged.connect(self.updateUI)
|
||||
|
||||
def updateUI(self):
|
||||
|
||||
typeSet = True
|
||||
if self.propertyIsEnumeration():
|
||||
self.form.labelEnum.setEnabled(True)
|
||||
self.form.propertyEnum.setEnabled(True)
|
||||
typeSet = self.form.propertyEnum.text().strip() != ""
|
||||
else:
|
||||
self.form.labelEnum.setEnabled(False)
|
||||
self.form.propertyEnum.setEnabled(False)
|
||||
if self.form.propertyEnum.text().strip():
|
||||
self.form.propertyEnum.setText("")
|
||||
|
||||
ok = self.form.buttonBox.button(QtGui.QDialogButtonBox.Ok)
|
||||
|
||||
if not re.match("^[A-Za-z0-9_]*$", self.form.propertyName.text()):
|
||||
typeSet = False
|
||||
|
||||
if typeSet and self.propertyGroup():
|
||||
ok.setEnabled(True)
|
||||
else:
|
||||
ok.setEnabled(False)
|
||||
|
||||
def propertyName(self):
|
||||
return self.form.propertyName.text().strip()
|
||||
|
||||
def propertyGroup(self):
|
||||
return self.form.propertyGroup.currentText().strip()
|
||||
|
||||
def propertyType(self):
|
||||
return PathPropertyBag.SupportedPropertyType[
|
||||
self.form.propertyType.currentText()
|
||||
].strip()
|
||||
|
||||
def propertyInfo(self):
|
||||
return self.form.propertyInfo.toPlainText().strip()
|
||||
|
||||
def createAnother(self):
|
||||
return self.form.createAnother.isChecked()
|
||||
|
||||
def propertyEnumerations(self):
|
||||
return [s.strip() for s in self.form.propertyEnum.text().strip().split(",")]
|
||||
|
||||
def propertyIsEnumeration(self):
|
||||
return self.propertyType() == "App::PropertyEnumeration"
|
||||
|
||||
def exec_(self, name):
|
||||
if name:
|
||||
# property exists - this is an edit operation
|
||||
self.form.propertyName.setText(name)
|
||||
if self.propertyIsEnumeration():
|
||||
self.form.propertyEnum.setText(
|
||||
",".join(self.obj.getEnumerationsOfProperty(name))
|
||||
)
|
||||
self.form.propertyInfo.setText(self.obj.getDocumentationOfProperty(name))
|
||||
|
||||
self.form.labelName.setEnabled(False)
|
||||
self.form.propertyName.setEnabled(False)
|
||||
self.form.labelType.setEnabled(False)
|
||||
self.form.propertyType.setEnabled(False)
|
||||
self.form.createAnother.setEnabled(False)
|
||||
|
||||
else:
|
||||
self.form.propertyName.setText("")
|
||||
self.form.propertyInfo.setText("")
|
||||
self.form.propertyEnum.setText("")
|
||||
# self.form.propertyName.setFocus()
|
||||
|
||||
self.updateUI()
|
||||
|
||||
return self.form.exec_()
|
||||
|
||||
|
||||
class TaskPanel(object):
|
||||
ColumnName = 0
|
||||
# ColumnType = 1
|
||||
ColumnVal = 1
|
||||
# TableHeaders = ['Property', 'Type', 'Value']
|
||||
TableHeaders = ["Property", "Value"]
|
||||
|
||||
def __init__(self, vobj):
|
||||
self.obj = vobj.Object
|
||||
self.props = sorted(self.obj.Proxy.getCustomProperties())
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":panels/PropertyBag.ui")
|
||||
|
||||
# initialized later
|
||||
self.model = None
|
||||
self.delegate = None
|
||||
FreeCAD.ActiveDocument.openTransaction("Edit PropertyBag")
|
||||
|
||||
def updateData(self, topLeft, bottomRight):
|
||||
pass
|
||||
|
||||
def _setupProperty(self, i, name):
|
||||
typ = PathPropertyBag.getPropertyTypeName(self.obj.getTypeIdOfProperty(name))
|
||||
val = PathUtil.getPropertyValueString(self.obj, name)
|
||||
info = self.obj.getDocumentationOfProperty(name)
|
||||
|
||||
self.model.setData(
|
||||
self.model.index(i, self.ColumnName), name, QtCore.Qt.EditRole
|
||||
)
|
||||
# self.model.setData(self.model.index(i, self.ColumnType), typ, QtCore.Qt.EditRole)
|
||||
self.model.setData(
|
||||
self.model.index(i, self.ColumnVal), self.obj, Delegate.RoleObject
|
||||
)
|
||||
self.model.setData(
|
||||
self.model.index(i, self.ColumnVal), name, Delegate.RoleProperty
|
||||
)
|
||||
self.model.setData(
|
||||
self.model.index(i, self.ColumnVal), val, QtCore.Qt.DisplayRole
|
||||
)
|
||||
|
||||
self.model.setData(
|
||||
self.model.index(i, self.ColumnName), typ, QtCore.Qt.ToolTipRole
|
||||
)
|
||||
# self.model.setData(self.model.index(i, self.ColumnType), info, QtCore.Qt.ToolTipRole)
|
||||
self.model.setData(
|
||||
self.model.index(i, self.ColumnVal), info, QtCore.Qt.ToolTipRole
|
||||
)
|
||||
|
||||
self.model.item(i, self.ColumnName).setEditable(False)
|
||||
# self.model.item(i, self.ColumnType).setEditable(False)
|
||||
|
||||
def setupUi(self):
|
||||
Path.Log.track()
|
||||
|
||||
self.delegate = Delegate(self.form)
|
||||
self.model = QtGui.QStandardItemModel(
|
||||
len(self.props), len(self.TableHeaders), self.form
|
||||
)
|
||||
self.model.setHorizontalHeaderLabels(self.TableHeaders)
|
||||
|
||||
for i, name in enumerate(self.props):
|
||||
self._setupProperty(i, name)
|
||||
|
||||
self.form.table.setModel(self.model)
|
||||
self.form.table.setItemDelegateForColumn(self.ColumnVal, self.delegate)
|
||||
self.form.table.resizeColumnsToContents()
|
||||
|
||||
self.model.dataChanged.connect(self.updateData)
|
||||
self.form.table.selectionModel().selectionChanged.connect(self.propertySelected)
|
||||
self.form.add.clicked.connect(self.propertyAdd)
|
||||
self.form.remove.clicked.connect(self.propertyRemove)
|
||||
self.form.modify.clicked.connect(self.propertyModify)
|
||||
self.form.table.doubleClicked.connect(self.propertyModifyIndex)
|
||||
self.propertySelected([])
|
||||
|
||||
def accept(self):
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCADGui.ActiveDocument.resetEdit()
|
||||
FreeCADGui.Control.closeDialog()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def reject(self):
|
||||
FreeCAD.ActiveDocument.abortTransaction()
|
||||
FreeCADGui.Control.closeDialog()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def propertySelected(self, selection):
|
||||
Path.Log.track()
|
||||
if selection:
|
||||
self.form.modify.setEnabled(True)
|
||||
self.form.remove.setEnabled(True)
|
||||
else:
|
||||
self.form.modify.setEnabled(False)
|
||||
self.form.remove.setEnabled(False)
|
||||
|
||||
def addCustomProperty(self, obj, dialog):
|
||||
name = dialog.propertyName()
|
||||
typ = dialog.propertyType()
|
||||
grp = dialog.propertyGroup()
|
||||
info = dialog.propertyInfo()
|
||||
propname = self.obj.Proxy.addCustomProperty(typ, name, grp, info)
|
||||
if dialog.propertyIsEnumeration():
|
||||
setattr(self.obj, name, dialog.propertyEnumerations())
|
||||
return (propname, info)
|
||||
|
||||
def propertyAdd(self):
|
||||
Path.Log.track()
|
||||
more = False
|
||||
grp = None
|
||||
typ = None
|
||||
while True:
|
||||
dialog = PropertyCreate(self.obj, grp, typ, more)
|
||||
if dialog.exec_(None):
|
||||
# if we block signals the view doesn't get updated, surprise, surprise
|
||||
# self.model.blockSignals(True)
|
||||
name, info = self.addCustomProperty(self.obj, dialog)
|
||||
index = 0
|
||||
for i in range(self.model.rowCount()):
|
||||
index = i
|
||||
if (
|
||||
self.model.item(i, self.ColumnName).data(QtCore.Qt.EditRole)
|
||||
> dialog.propertyName()
|
||||
):
|
||||
break
|
||||
self.model.insertRows(index, 1)
|
||||
self._setupProperty(index, name)
|
||||
self.form.table.selectionModel().setCurrentIndex(
|
||||
self.model.index(index, 0), QtCore.QItemSelectionModel.Rows
|
||||
)
|
||||
# self.model.blockSignals(False)
|
||||
more = dialog.createAnother()
|
||||
else:
|
||||
more = False
|
||||
if not more:
|
||||
break
|
||||
|
||||
def propertyModifyIndex(self, index):
|
||||
Path.Log.track(index.row(), index.column())
|
||||
row = index.row()
|
||||
|
||||
obj = self.model.item(row, self.ColumnVal).data(Delegate.RoleObject)
|
||||
nam = self.model.item(row, self.ColumnVal).data(Delegate.RoleProperty)
|
||||
grp = obj.getGroupOfProperty(nam)
|
||||
typ = obj.getTypeIdOfProperty(nam)
|
||||
|
||||
dialog = PropertyCreate(self.obj, grp, typ, False)
|
||||
if dialog.exec_(nam):
|
||||
val = getattr(obj, nam)
|
||||
obj.removeProperty(nam)
|
||||
name, info = self.addCustomProperty(self.obj, dialog)
|
||||
try:
|
||||
setattr(obj, nam, val)
|
||||
except Exception:
|
||||
# this can happen if the old enumeration value doesn't exist anymore
|
||||
pass
|
||||
newVal = PathUtil.getPropertyValueString(obj, nam)
|
||||
self.model.setData(
|
||||
self.model.index(row, self.ColumnVal), newVal, QtCore.Qt.DisplayRole
|
||||
)
|
||||
|
||||
# self.model.setData(self.model.index(row, self.ColumnType), info, QtCore.Qt.ToolTipRole)
|
||||
self.model.setData(
|
||||
self.model.index(row, self.ColumnVal), info, QtCore.Qt.ToolTipRole
|
||||
)
|
||||
|
||||
def propertyModify(self):
|
||||
Path.Log.track()
|
||||
rows = []
|
||||
for index in self.form.table.selectionModel().selectedIndexes():
|
||||
row = index.row()
|
||||
if row in rows:
|
||||
continue
|
||||
rows.append(row)
|
||||
|
||||
self.propertyModifyIndex(index)
|
||||
|
||||
def propertyRemove(self):
|
||||
Path.Log.track()
|
||||
# first find all rows which need to be removed
|
||||
rows = []
|
||||
for index in self.form.table.selectionModel().selectedIndexes():
|
||||
if not index.row() in rows:
|
||||
rows.append(index.row())
|
||||
|
||||
# then remove them in reverse order so the indexes of the remaining rows
|
||||
# to delete are still valid
|
||||
for row in reversed(sorted(rows)):
|
||||
self.obj.removeProperty(self.model.item(row).data(QtCore.Qt.EditRole))
|
||||
self.model.removeRow(row)
|
||||
|
||||
|
||||
def Create(name="PropertyBag"):
|
||||
"""Create(name = 'PropertyBag') ... creates a new setup sheet"""
|
||||
FreeCAD.ActiveDocument.openTransaction("Create PropertyBag")
|
||||
pcont = PathPropertyBag.Create(name)
|
||||
PathIconViewProvider.Attach(pcont.ViewObject, name)
|
||||
return pcont
|
||||
|
||||
|
||||
PathIconViewProvider.RegisterViewProvider("PropertyBag", ViewProvider)
|
||||
|
||||
|
||||
class PropertyBagCreateCommand(object):
|
||||
"""Command to create a property container object"""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetResources(self):
|
||||
return {
|
||||
"MenuText": translate("Path_PropertyBag", "PropertyBag"),
|
||||
"ToolTip": translate(
|
||||
"Path_PropertyBag",
|
||||
"Creates an object which can be used to store reference properties.",
|
||||
),
|
||||
}
|
||||
|
||||
def IsActive(self):
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
sel = FreeCADGui.Selection.getSelectionEx()
|
||||
obj = Create()
|
||||
body = None
|
||||
if sel:
|
||||
if "PartDesign::Body" == sel[0].Object.TypeId:
|
||||
body = sel[0].Object
|
||||
elif hasattr(sel[0].Object, "getParentGeoFeatureGroup"):
|
||||
body = sel[0].Object.getParentGeoFeatureGroup()
|
||||
if body:
|
||||
obj.Label = "Attributes"
|
||||
group = body.Group
|
||||
group.append(obj)
|
||||
body.Group = group
|
||||
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand("Path_PropertyBag", PropertyBagCreateCommand())
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathPropertyBagGui ... done\n")
|
||||
@@ -1,239 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2020 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 Path
|
||||
import PathScripts.PathSetupSheetOpPrototype as PathSetupSheetOpPrototype
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
__title__ = "Path Property Editor"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Task panel editor for Properties"
|
||||
|
||||
|
||||
if False:
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
else:
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
|
||||
|
||||
class _PropertyEditor(object):
|
||||
"""Base class of all property editors - just outlines the TableView delegate interface."""
|
||||
|
||||
def __init__(self, obj, prop):
|
||||
self.obj = obj
|
||||
self.prop = prop
|
||||
|
||||
def widget(self, parent):
|
||||
"""widget(parent) ... called by the delegate to get a new editor widget.
|
||||
Must be implemented by subclasses and return the widget."""
|
||||
pass
|
||||
|
||||
def setEditorData(self, widget):
|
||||
"""setEditorData(widget) ... called by the delegate to initialize the editor.
|
||||
The widget is the object returned by widget().
|
||||
Must be implemented by subclasses."""
|
||||
pass
|
||||
|
||||
def setModelData(self, widget):
|
||||
"""setModelData(widget) ... called by the delegate to store new values.
|
||||
Must be implemented by subclasses."""
|
||||
pass
|
||||
|
||||
def propertyValue(self):
|
||||
return self.obj.getPropertyByName(self.prop)
|
||||
|
||||
def setProperty(self, value):
|
||||
setattr(self.obj, self.prop, value)
|
||||
|
||||
def displayString(self):
|
||||
return self.propertyValue()
|
||||
|
||||
|
||||
class _PropertyEditorBool(_PropertyEditor):
|
||||
"""Editor for boolean values - uses a combo box."""
|
||||
|
||||
def widget(self, parent):
|
||||
return QtGui.QComboBox(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
widget.clear()
|
||||
widget.addItems([str(False), str(True)])
|
||||
index = 1 if self.propertyValue() else 0
|
||||
widget.setCurrentIndex(index)
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.setProperty(widget.currentText() == str(True))
|
||||
|
||||
|
||||
class _PropertyEditorString(_PropertyEditor):
|
||||
"""Editor for string values - uses a line edit."""
|
||||
|
||||
def widget(self, parent):
|
||||
return QtGui.QLineEdit(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
text = "" if self.propertyValue() is None else self.propertyValue()
|
||||
widget.setText(text)
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.setProperty(widget.text())
|
||||
|
||||
|
||||
class _PropertyEditorQuantity(_PropertyEditor):
|
||||
def widget(self, parent):
|
||||
return QtGui.QLineEdit(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
quantity = self.propertyValue()
|
||||
if quantity is None:
|
||||
quantity = self.defaultQuantity()
|
||||
widget.setText(quantity.getUserPreferred()[0])
|
||||
|
||||
def defaultQuantity(self):
|
||||
pass
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.setProperty(FreeCAD.Units.Quantity(widget.text()))
|
||||
|
||||
def displayString(self):
|
||||
if self.propertyValue() is None:
|
||||
return ""
|
||||
return self.propertyValue().getUserPreferred()[0]
|
||||
|
||||
|
||||
class _PropertyEditorAngle(_PropertyEditorQuantity):
|
||||
"""Editor for angle values - uses a line edit"""
|
||||
|
||||
def defaultQuantity(self):
|
||||
return FreeCAD.Units.Quantity(0, FreeCAD.Units.Angle)
|
||||
|
||||
|
||||
class _PropertyEditorLength(_PropertyEditorQuantity):
|
||||
"""Editor for length values - uses a line edit."""
|
||||
|
||||
def defaultQuantity(self):
|
||||
return FreeCAD.Units.Quantity(0, FreeCAD.Units.Length)
|
||||
|
||||
|
||||
class _PropertyEditorPercent(_PropertyEditor):
|
||||
"""Editor for percent values - uses a spin box."""
|
||||
|
||||
def widget(self, parent):
|
||||
return QtGui.QSpinBox(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
widget.setRange(0, 100)
|
||||
value = self.propertyValue()
|
||||
if value is None:
|
||||
value = 0
|
||||
widget.setValue(value)
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.setProperty(widget.value())
|
||||
|
||||
|
||||
class _PropertyEditorInteger(_PropertyEditor):
|
||||
"""Editor for integer values - uses a spin box."""
|
||||
|
||||
def widget(self, parent):
|
||||
return QtGui.QSpinBox(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
value = self.propertyValue()
|
||||
if value is None:
|
||||
value = 0
|
||||
widget.setValue(value)
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.setProperty(widget.value())
|
||||
|
||||
|
||||
class _PropertyEditorFloat(_PropertyEditor):
|
||||
"""Editor for float values - uses a double spin box."""
|
||||
|
||||
def widget(self, parent):
|
||||
return QtGui.QDoubleSpinBox(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
value = self.propertyValue()
|
||||
if value is None:
|
||||
value = 0.0
|
||||
widget.setValue(value)
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.setProperty(widget.value())
|
||||
|
||||
|
||||
class _PropertyEditorFile(_PropertyEditor):
|
||||
def widget(self, parent):
|
||||
return QtGui.QLineEdit(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
text = "" if self.propertyValue() is None else self.propertyValue()
|
||||
widget.setText(text)
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.setProperty(widget.text())
|
||||
|
||||
|
||||
class _PropertyEditorEnumeration(_PropertyEditor):
|
||||
def widget(self, parent):
|
||||
return QtGui.QComboBox(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
widget.clear()
|
||||
widget.addItems(self.obj.getEnumerationsOfProperty(self.prop))
|
||||
widget.setCurrentText(self.propertyValue())
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.setProperty(widget.currentText())
|
||||
|
||||
|
||||
_EditorFactory = {
|
||||
"App::PropertyAngle": _PropertyEditorAngle,
|
||||
"App::PropertyBool": _PropertyEditorBool,
|
||||
"App::PropertyDistance": _PropertyEditorLength,
|
||||
"App::PropertyEnumeration": _PropertyEditorEnumeration,
|
||||
#'App::PropertyFile' : _PropertyEditorFile,
|
||||
"App::PropertyFloat": _PropertyEditorFloat,
|
||||
"App::PropertyInteger": _PropertyEditorInteger,
|
||||
"App::PropertyLength": _PropertyEditorLength,
|
||||
"App::PropertyPercent": _PropertyEditorPercent,
|
||||
"App::PropertyString": _PropertyEditorString,
|
||||
}
|
||||
|
||||
|
||||
def Types():
|
||||
"""Return the types of properties supported."""
|
||||
return [t for t in _EditorFactory]
|
||||
|
||||
|
||||
def Editor(obj, prop):
|
||||
"""Returns an editor class to be used for the given property."""
|
||||
factory = _EditorFactory[obj.getTypeIdOfProperty(prop)]
|
||||
if factory:
|
||||
return factory(obj, prop)
|
||||
return None
|
||||
@@ -35,7 +35,6 @@ import FreeCADGui
|
||||
import Path
|
||||
import Path.Base.Util as PathUtil
|
||||
import PathScripts
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
from collections import Counter
|
||||
from datetime import datetime
|
||||
import os
|
||||
@@ -55,10 +54,10 @@ class CommandPathSanity:
|
||||
def resolveOutputPath(self, job):
|
||||
if job.PostProcessorOutputFile != "":
|
||||
filepath = job.PostProcessorOutputFile
|
||||
elif PathPreferences.defaultOutputFile() != "":
|
||||
filepath = PathPreferences.defaultOutputFile()
|
||||
elif Path.Preferences.defaultOutputFile() != "":
|
||||
filepath = Path.Preferences.defaultOutputFile()
|
||||
else:
|
||||
filepath = PathPreferences.macroFilePath()
|
||||
filepath = Path.Preferences.macroFilePath()
|
||||
|
||||
if "%D" in filepath:
|
||||
D = FreeCAD.ActiveDocument.FileName
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import Path
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.drillableLib as drillableLib
|
||||
import math
|
||||
|
||||
@@ -255,61 +254,61 @@ class ALLGate(PathBaseGate):
|
||||
|
||||
def contourselect():
|
||||
FreeCADGui.Selection.addSelectionGate(CONTOURGate())
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Contour Select Mode\n")
|
||||
|
||||
|
||||
def eselect():
|
||||
FreeCADGui.Selection.addSelectionGate(EGate())
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Edge Select Mode\n")
|
||||
|
||||
|
||||
def drillselect():
|
||||
FreeCADGui.Selection.addSelectionGate(DRILLGate())
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Drilling Select Mode\n")
|
||||
|
||||
|
||||
def engraveselect():
|
||||
FreeCADGui.Selection.addSelectionGate(ENGRAVEGate())
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Engraving Select Mode\n")
|
||||
|
||||
|
||||
def fselect():
|
||||
FreeCADGui.Selection.addSelectionGate(FACEGate()) # Was PROFILEGate()
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Profiling Select Mode\n")
|
||||
|
||||
|
||||
def chamferselect():
|
||||
FreeCADGui.Selection.addSelectionGate(CHAMFERGate())
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Deburr Select Mode\n")
|
||||
|
||||
|
||||
def profileselect():
|
||||
FreeCADGui.Selection.addSelectionGate(PROFILEGate())
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Profiling Select Mode\n")
|
||||
|
||||
|
||||
def pocketselect():
|
||||
FreeCADGui.Selection.addSelectionGate(POCKETGate())
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Pocketing Select Mode\n")
|
||||
|
||||
|
||||
def adaptiveselect():
|
||||
FreeCADGui.Selection.addSelectionGate(ADAPTIVEGate())
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Adaptive Select Mode\n")
|
||||
|
||||
|
||||
def slotselect():
|
||||
FreeCADGui.Selection.addSelectionGate(ALLGate())
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Slot Cutter Select Mode\n")
|
||||
|
||||
|
||||
@@ -318,30 +317,30 @@ def surfaceselect():
|
||||
if MESHGate() or FACEGate():
|
||||
gate = True
|
||||
FreeCADGui.Selection.addSelectionGate(gate)
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Surfacing Select Mode\n")
|
||||
|
||||
|
||||
def vcarveselect():
|
||||
FreeCADGui.Selection.addSelectionGate(VCARVEGate())
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Vcarve Select Mode\n")
|
||||
|
||||
|
||||
def probeselect():
|
||||
FreeCADGui.Selection.addSelectionGate(PROBEGate())
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Probe Select Mode\n")
|
||||
|
||||
|
||||
def customselect():
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Custom Select Mode\n")
|
||||
|
||||
|
||||
def turnselect():
|
||||
FreeCADGui.Selection.addSelectionGate(TURNGate())
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Turning Select Mode\n")
|
||||
|
||||
|
||||
@@ -377,5 +376,5 @@ def select(op):
|
||||
|
||||
def clear():
|
||||
FreeCADGui.Selection.removeSelectionGate()
|
||||
if not PathPreferences.suppressSelectionModeWarning():
|
||||
if not Path.Preferences.suppressSelectionModeWarning():
|
||||
FreeCAD.Console.PrintWarning("Free Select\n")
|
||||
|
||||
@@ -1,461 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2017 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 Path
|
||||
import Path.Base.Util as PathUtil
|
||||
import PathScripts.PathSetupSheetOpPrototype as PathSetupSheetOpPrototype
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
__title__ = "Setup Sheet for a Job."
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "A container for all default values and job specific configuration values."
|
||||
|
||||
_RegisteredOps: dict = {}
|
||||
|
||||
|
||||
if False:
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
else:
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
|
||||
|
||||
class Template:
|
||||
|
||||
HorizRapid = "HorizRapid"
|
||||
VertRapid = "VertRapid"
|
||||
CoolantMode = "CoolantMode"
|
||||
SafeHeightOffset = "SafeHeightOffset"
|
||||
SafeHeightExpression = "SafeHeightExpression"
|
||||
ClearanceHeightOffset = "ClearanceHeightOffset"
|
||||
ClearanceHeightExpression = "ClearanceHeightExpression"
|
||||
StartDepthExpression = "StartDepthExpression"
|
||||
FinalDepthExpression = "FinalDepthExpression"
|
||||
StepDownExpression = "StepDownExpression"
|
||||
Fixtures = "Fixtures"
|
||||
OrderOutputBy = "OrderOutputBy"
|
||||
SplitOutput = "SplitOutput"
|
||||
|
||||
All = [
|
||||
HorizRapid,
|
||||
VertRapid,
|
||||
CoolantMode,
|
||||
SafeHeightOffset,
|
||||
SafeHeightExpression,
|
||||
ClearanceHeightOffset,
|
||||
ClearanceHeightExpression,
|
||||
StartDepthExpression,
|
||||
FinalDepthExpression,
|
||||
StepDownExpression,
|
||||
]
|
||||
|
||||
|
||||
def _traverseTemplateAttributes(attrs, codec):
|
||||
Path.Log.debug(attrs)
|
||||
coded = {}
|
||||
for key, value in PathUtil.keyValueIter(attrs):
|
||||
if type(value) == dict:
|
||||
Path.Log.debug("%s is a dict" % key)
|
||||
coded[key] = _traverseTemplateAttributes(value, codec)
|
||||
elif type(value) == list:
|
||||
Path.Log.debug("%s is a list" % key)
|
||||
coded[key] = [_traverseTemplateAttributes(attr, codec) for attr in value]
|
||||
elif PathUtil.isString(value):
|
||||
Path.Log.debug("%s is a string" % key)
|
||||
coded[key] = codec(value)
|
||||
else:
|
||||
Path.Log.debug("%s is %s" % (key, type(value)))
|
||||
coded[key] = value
|
||||
return coded
|
||||
|
||||
|
||||
class SetupSheet:
|
||||
"""Property container object used by a Job to hold global reference values."""
|
||||
|
||||
TemplateReference = "${SetupSheet}"
|
||||
|
||||
DefaultSafeHeightOffset = "3 mm"
|
||||
DefaultClearanceHeightOffset = "5 mm"
|
||||
DefaultSafeHeightExpression = "OpStockZMax+${SetupSheet}.SafeHeightOffset"
|
||||
DefaultClearanceHeightExpression = "OpStockZMax+${SetupSheet}.ClearanceHeightOffset"
|
||||
|
||||
DefaultStartDepthExpression = "OpStartDepth"
|
||||
DefaultFinalDepthExpression = "OpFinalDepth"
|
||||
DefaultStepDownExpression = "OpToolDiameter"
|
||||
|
||||
DefaultCoolantModes = ["None", "Flood", "Mist"]
|
||||
|
||||
def __init__(self, obj):
|
||||
self.obj = obj
|
||||
obj.addProperty(
|
||||
"App::PropertySpeed",
|
||||
"VertRapid",
|
||||
"ToolController",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Default speed for horizontal rapid moves."
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertySpeed",
|
||||
"HorizRapid",
|
||||
"ToolController",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Default speed for vertical rapid moves."
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyStringList",
|
||||
"CoolantModes",
|
||||
"CoolantMode",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Coolant Modes"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"CoolantMode",
|
||||
"CoolantMode",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Default coolant mode."),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLength",
|
||||
"SafeHeightOffset",
|
||||
"OperationHeights",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"The usage of this field depends on SafeHeightExpression - by default its value is added to StartDepth and used for SafeHeight of an operation.",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"SafeHeightExpression",
|
||||
"OperationHeights",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Expression set for the SafeHeight of new operations."
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLength",
|
||||
"ClearanceHeightOffset",
|
||||
"OperationHeights",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"The usage of this field depends on ClearanceHeightExpression - by default is value is added to StartDepth and used for ClearanceHeight of an operation.",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"ClearanceHeightExpression",
|
||||
"OperationHeights",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Expression set for the ClearanceHeight of new operations.",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"StartDepthExpression",
|
||||
"OperationDepths",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Expression used for StartDepth of new operations."
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"FinalDepthExpression",
|
||||
"OperationDepths",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Expression used for FinalDepth of new operations."
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"StepDownExpression",
|
||||
"OperationDepths",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Expression used for StepDown of new operations."
|
||||
),
|
||||
)
|
||||
|
||||
obj.SafeHeightOffset = self.decodeAttributeString(self.DefaultSafeHeightOffset)
|
||||
obj.ClearanceHeightOffset = self.decodeAttributeString(
|
||||
self.DefaultClearanceHeightOffset
|
||||
)
|
||||
obj.SafeHeightExpression = self.decodeAttributeString(
|
||||
self.DefaultSafeHeightExpression
|
||||
)
|
||||
obj.ClearanceHeightExpression = self.decodeAttributeString(
|
||||
self.DefaultClearanceHeightExpression
|
||||
)
|
||||
|
||||
obj.StartDepthExpression = self.decodeAttributeString(
|
||||
self.DefaultStartDepthExpression
|
||||
)
|
||||
obj.FinalDepthExpression = self.decodeAttributeString(
|
||||
self.DefaultFinalDepthExpression
|
||||
)
|
||||
obj.StepDownExpression = self.decodeAttributeString(
|
||||
self.DefaultStepDownExpression
|
||||
)
|
||||
|
||||
obj.CoolantModes = self.DefaultCoolantModes
|
||||
obj.CoolantMode = self.DefaultCoolantModes
|
||||
|
||||
obj.Proxy = self
|
||||
|
||||
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 hasDefaultToolRapids(self):
|
||||
return Path.Geom.isRoughly(self.obj.VertRapid.Value, 0) and Path.Geom.isRoughly(
|
||||
self.obj.HorizRapid.Value, 0
|
||||
)
|
||||
|
||||
def hasDefaultOperationHeights(self):
|
||||
if (
|
||||
self.obj.SafeHeightOffset.UserString
|
||||
!= FreeCAD.Units.Quantity(self.DefaultSafeHeightOffset).UserString
|
||||
):
|
||||
return False
|
||||
if (
|
||||
self.obj.ClearanceHeightOffset.UserString
|
||||
!= FreeCAD.Units.Quantity(self.DefaultClearanceHeightOffset).UserString
|
||||
):
|
||||
return False
|
||||
if self.obj.SafeHeightExpression != self.decodeAttributeString(
|
||||
self.DefaultSafeHeightExpression
|
||||
):
|
||||
return False
|
||||
if self.obj.ClearanceHeightExpression != self.decodeAttributeString(
|
||||
self.DefaultClearanceHeightExpression
|
||||
):
|
||||
return False
|
||||
return True
|
||||
|
||||
def hasDefaultOperationDepths(self):
|
||||
if self.obj.StartDepthExpression != self.DefaultStartDepthExpression:
|
||||
return False
|
||||
if self.obj.FinalDepthExpression != self.DefaultFinalDepthExpression:
|
||||
return False
|
||||
if self.obj.StepDownExpression != self.DefaultStepDownExpression:
|
||||
return False
|
||||
return True
|
||||
|
||||
def hasDefaultCoolantMode(self):
|
||||
return self.obj.CoolantMode == "None"
|
||||
|
||||
def setFromTemplate(self, attrs):
|
||||
"""setFromTemplate(attrs) ... sets the default values from the given dictionary."""
|
||||
for name in Template.All:
|
||||
if attrs.get(name) is not None:
|
||||
setattr(self.obj, name, attrs[name])
|
||||
|
||||
for opName, op in PathUtil.keyValueIter(_RegisteredOps):
|
||||
opSetting = attrs.get(opName)
|
||||
if opSetting is not None:
|
||||
prototype = op.prototype(opName)
|
||||
for propName in op.properties():
|
||||
value = opSetting.get(propName)
|
||||
if value is not None:
|
||||
prop = prototype.getProperty(propName)
|
||||
propertyName = OpPropertyName(opName, propName)
|
||||
propertyGroup = OpPropertyGroup(opName)
|
||||
prop.setupProperty(
|
||||
self.obj,
|
||||
propertyName,
|
||||
propertyGroup,
|
||||
prop.valueFromString(value),
|
||||
)
|
||||
|
||||
def templateAttributes(
|
||||
self,
|
||||
includeRapids=True,
|
||||
includeCoolantMode=True,
|
||||
includeHeights=True,
|
||||
includeDepths=True,
|
||||
includeOps=None,
|
||||
):
|
||||
"""templateAttributes(includeRapids, includeHeights, includeDepths) ... answers a dictionary with the default values."""
|
||||
attrs = {}
|
||||
|
||||
if includeRapids:
|
||||
attrs[Template.VertRapid] = self.obj.VertRapid.UserString
|
||||
attrs[Template.HorizRapid] = self.obj.HorizRapid.UserString
|
||||
|
||||
if includeCoolantMode:
|
||||
attrs[Template.CoolantMode] = self.obj.CoolantMode
|
||||
|
||||
if includeHeights:
|
||||
attrs[Template.SafeHeightOffset] = self.obj.SafeHeightOffset.UserString
|
||||
attrs[Template.SafeHeightExpression] = self.obj.SafeHeightExpression
|
||||
attrs[
|
||||
Template.ClearanceHeightOffset
|
||||
] = self.obj.ClearanceHeightOffset.UserString
|
||||
attrs[
|
||||
Template.ClearanceHeightExpression
|
||||
] = self.obj.ClearanceHeightExpression
|
||||
|
||||
if includeDepths:
|
||||
attrs[Template.StartDepthExpression] = self.obj.StartDepthExpression
|
||||
attrs[Template.FinalDepthExpression] = self.obj.FinalDepthExpression
|
||||
attrs[Template.StepDownExpression] = self.obj.StepDownExpression
|
||||
|
||||
if includeOps:
|
||||
for opName in includeOps:
|
||||
settings = {}
|
||||
op = _RegisteredOps[opName]
|
||||
for propName in op.properties():
|
||||
prop = OpPropertyName(opName, propName)
|
||||
if hasattr(self.obj, prop):
|
||||
settings[propName] = PathUtil.getPropertyValueString(
|
||||
self.obj, prop
|
||||
)
|
||||
attrs[opName] = settings
|
||||
|
||||
return attrs
|
||||
|
||||
def expressionReference(self):
|
||||
"""expressionReference() ... returns the string to be used in expressions"""
|
||||
# Using the Name here and not the Label (both would be valid) because the Name 'fails early'.
|
||||
#
|
||||
# If there is a Name/Label conflict and an expression is bound to the Name we'll get an error
|
||||
# on creation (Property not found). Not good, but at least there's some indication that
|
||||
# something's afoul.
|
||||
#
|
||||
# If the expression is based on the Label everything works out nicely - until the document is
|
||||
# saved and loaded from disk. The Labels change in order to avoid the Name/Label conflict
|
||||
# but the expression stays the same. If the user's lucky the expression is broken because the
|
||||
# conflicting object doesn't have the properties reference by the expressions. If the user is
|
||||
# not so lucky those properties also exist in the other object, there is no indication that
|
||||
# anything is wrong but the expressions will substitute the values from the wrong object.
|
||||
#
|
||||
# I prefer the question: "why do I get this error when I create ..." over "my cnc machine just
|
||||
# rammed it's tool head into the table ..." or even "I saved my file and now it's corrupt..."
|
||||
#
|
||||
# https://forum.freecadweb.org/viewtopic.php?f=10&t=24839
|
||||
# https://forum.freecadweb.org/viewtopic.php?f=10&t=24845
|
||||
return self.obj.Name
|
||||
|
||||
def encodeAttributeString(self, attr):
|
||||
"""encodeAttributeString(attr) ... return the encoded string of a template attribute."""
|
||||
return PathUtil.toUnicode(
|
||||
attr.replace(self.expressionReference(), self.TemplateReference)
|
||||
)
|
||||
|
||||
def decodeAttributeString(self, attr):
|
||||
"""decodeAttributeString(attr) ... return the decoded string of a template attribute."""
|
||||
return PathUtil.toUnicode(
|
||||
attr.replace(self.TemplateReference, self.expressionReference())
|
||||
)
|
||||
|
||||
def encodeTemplateAttributes(self, attrs):
|
||||
"""encodeTemplateAttributes(attrs) ... return a dictionary with all values encoded."""
|
||||
return _traverseTemplateAttributes(attrs, self.encodeAttributeString)
|
||||
|
||||
def decodeTemplateAttributes(self, attrs):
|
||||
"""decodeTemplateAttributes(attrs) ... expand template attributes to reference the receiver where applicable."""
|
||||
return _traverseTemplateAttributes(attrs, self.decodeAttributeString)
|
||||
|
||||
def operationsWithSettings(self):
|
||||
"""operationsWithSettings() ... returns a list of operations which currently have some settings defined."""
|
||||
ops = []
|
||||
for name, value in PathUtil.keyValueIter(_RegisteredOps):
|
||||
for prop in value.registeredPropertyNames(name):
|
||||
if hasattr(self.obj, prop):
|
||||
ops.append(name)
|
||||
break
|
||||
return list(sorted(ops))
|
||||
|
||||
def setOperationProperties(self, obj, opName):
|
||||
Path.Log.track(obj.Label, opName)
|
||||
try:
|
||||
op = _RegisteredOps[opName]
|
||||
for prop in op.properties():
|
||||
propName = OpPropertyName(opName, prop)
|
||||
if hasattr(self.obj, propName):
|
||||
setattr(obj, prop, getattr(self.obj, propName))
|
||||
except Exception:
|
||||
Path.Log.info("SetupSheet has no support for {}".format(opName))
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
|
||||
if not hasattr(obj, "CoolantModes"):
|
||||
obj.addProperty(
|
||||
"App::PropertyStringList",
|
||||
"CoolantModes",
|
||||
"CoolantMode",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Coolant Modes"),
|
||||
)
|
||||
obj.CoolantModes = self.DefaultCoolantModes
|
||||
|
||||
if not hasattr(obj, "CoolantMode"):
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"CoolantMode",
|
||||
"CoolantMode",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Default coolant mode."),
|
||||
)
|
||||
obj.CoolantMode = self.DefaultCoolantModes
|
||||
|
||||
|
||||
def Create(name="SetupSheet"):
|
||||
obj = FreeCAD.ActiveDocument.addObject("App::FeaturePython", name)
|
||||
obj.Proxy = SetupSheet(obj)
|
||||
return obj
|
||||
|
||||
|
||||
class _RegisteredOp(object):
|
||||
def __init__(self, factory, properties):
|
||||
self.factory = factory
|
||||
self.properties = properties
|
||||
|
||||
def registeredPropertyNames(self, name):
|
||||
return [OpPropertyName(name, prop) for prop in self.properties()]
|
||||
|
||||
def prototype(self, name):
|
||||
ptt = PathSetupSheetOpPrototype.OpPrototype(name)
|
||||
self.factory("OpPrototype.%s" % name, ptt)
|
||||
return ptt
|
||||
|
||||
|
||||
def RegisterOperation(name, objFactory, setupProperties):
|
||||
global _RegisteredOps
|
||||
_RegisteredOps[name] = _RegisteredOp(objFactory, setupProperties)
|
||||
|
||||
|
||||
def OpNamePrefix(name):
|
||||
return name.replace("Path", "").replace(" ", "").replace("_", "")
|
||||
|
||||
|
||||
def OpPropertyName(opName, propName):
|
||||
return "{}{}".format(OpNamePrefix(opName), propName)
|
||||
|
||||
|
||||
def OpPropertyGroup(opName):
|
||||
return "Op {}".format(opName)
|
||||
@@ -1,425 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2018 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 Path.Base.Util as PathUtil
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathScripts.PathGui as PathGui
|
||||
import PathScripts.PathIconViewProvider as PathIconViewProvider
|
||||
import PathScripts.PathSetupSheet as PathSetupSheet
|
||||
import PathScripts.PathSetupSheetOpPrototypeGui as PathSetupSheetOpPrototypeGui
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
__title__ = "Setup Sheet Editor"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Task panel editor for a SetupSheet"
|
||||
|
||||
|
||||
LOGLEVEL = False
|
||||
|
||||
if LOGLEVEL:
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
else:
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
|
||||
|
||||
class ViewProvider:
|
||||
"""ViewProvider for a SetupSheet.
|
||||
It's sole job is to provide an icon and invoke the TaskPanel on edit."""
|
||||
|
||||
def __init__(self, vobj, name):
|
||||
Path.Log.track(name)
|
||||
vobj.Proxy = self
|
||||
self.icon = name
|
||||
# mode = 2
|
||||
self.obj = None
|
||||
self.vobj = None
|
||||
|
||||
def attach(self, vobj):
|
||||
Path.Log.track()
|
||||
self.vobj = vobj
|
||||
self.obj = vobj.Object
|
||||
|
||||
def getIcon(self):
|
||||
return ":/icons/Path_SetupSheet.svg"
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
return None
|
||||
|
||||
def getDisplayMode(self, mode):
|
||||
return "Default"
|
||||
|
||||
def setEdit(self, vobj, mode=0):
|
||||
Path.Log.track()
|
||||
taskPanel = TaskPanel(vobj)
|
||||
FreeCADGui.Control.closeDialog()
|
||||
FreeCADGui.Control.showDialog(taskPanel)
|
||||
taskPanel.setupUi()
|
||||
return True
|
||||
|
||||
def unsetEdit(self, vobj, mode):
|
||||
FreeCADGui.Control.closeDialog()
|
||||
return
|
||||
|
||||
def claimChildren(self):
|
||||
return []
|
||||
|
||||
def doubleClicked(self, vobj):
|
||||
self.setEdit(vobj)
|
||||
|
||||
|
||||
class Delegate(QtGui.QStyledItemDelegate):
|
||||
PropertyRole = QtCore.Qt.UserRole + 1
|
||||
EditorRole = QtCore.Qt.UserRole + 2
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
if index.data(self.EditorRole) is None:
|
||||
editor = PathSetupSheetOpPrototypeGui.Editor(index.data(self.PropertyRole))
|
||||
index.model().setData(index, editor, self.EditorRole)
|
||||
return index.data(self.EditorRole).widget(parent)
|
||||
|
||||
def setEditorData(self, widget, index):
|
||||
Path.Log.track(index.row(), index.column())
|
||||
index.data(self.EditorRole).setEditorData(widget)
|
||||
|
||||
def setModelData(self, widget, model, index):
|
||||
Path.Log.track(index.row(), index.column())
|
||||
editor = index.data(self.EditorRole)
|
||||
editor.setModelData(widget)
|
||||
index.model().setData(index, editor.prop.displayString(), QtCore.Qt.DisplayRole)
|
||||
|
||||
def updateEditorGeometry(self, widget, option, index):
|
||||
widget.setGeometry(option.rect)
|
||||
|
||||
|
||||
class OpTaskPanel:
|
||||
"""Editor for an operation's property default values.
|
||||
The implementation is a simplified generic property editor with basically 3 fields
|
||||
- checkbox - if set a default value for the given property is set
|
||||
- name - a non-editable string with the property name
|
||||
- value - the actual editor for the property's default value
|
||||
The specific editor classes for a given property type are implemented in
|
||||
PathSetupSheetOpPrototypeGui which also provides a factory function. The properties
|
||||
are displayed in a table, each field occypying a column and each row representing
|
||||
a single property."""
|
||||
|
||||
def __init__(self, obj, name, op):
|
||||
self.name = name
|
||||
self.obj = obj
|
||||
self.op = op
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":/panels/SetupOp.ui")
|
||||
self.form.setWindowTitle("Op {}".format(self.name))
|
||||
self.props = sorted(op.properties())
|
||||
self.prototype = op.prototype(name)
|
||||
|
||||
# initialized later
|
||||
self.delegate = None
|
||||
self.model = None
|
||||
|
||||
def updateData(self, topLeft, bottomRight):
|
||||
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 setupUi(self):
|
||||
Path.Log.track()
|
||||
|
||||
self.delegate = Delegate(self.form)
|
||||
self.model = QtGui.QStandardItemModel(len(self.props), 3, self.form)
|
||||
self.model.setHorizontalHeaderLabels(["Set", "Property", "Value"])
|
||||
|
||||
for i, name in enumerate(self.props):
|
||||
prop = self.prototype.getProperty(name)
|
||||
isset = hasattr(self.obj, self.propertyName(name))
|
||||
if isset:
|
||||
prop.setValue(getattr(self.obj, self.propertyName(name)))
|
||||
|
||||
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, 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)
|
||||
|
||||
self.form.table.setModel(self.model)
|
||||
self.form.table.setItemDelegateForColumn(2, self.delegate)
|
||||
self.form.table.resizeColumnsToContents()
|
||||
|
||||
self.model.dataChanged.connect(self.updateData)
|
||||
|
||||
def propertyName(self, prop):
|
||||
return PathSetupSheet.OpPropertyName(self.name, prop)
|
||||
|
||||
def propertyGroup(self):
|
||||
return PathSetupSheet.OpPropertyGroup(self.name)
|
||||
|
||||
def accept(self):
|
||||
propertiesCreatedRemoved = False
|
||||
for i, name in enumerate(self.props):
|
||||
prop = self.prototype.getProperty(name)
|
||||
propName = self.propertyName(name)
|
||||
enabled = self.model.item(i, 0).checkState() == QtCore.Qt.Checked
|
||||
if enabled and not prop.getValue() is None:
|
||||
if prop.setupProperty(
|
||||
self.obj, propName, self.propertyGroup(), prop.getValue()
|
||||
):
|
||||
propertiesCreatedRemoved = True
|
||||
else:
|
||||
if hasattr(self.obj, propName):
|
||||
self.obj.removeProperty(propName)
|
||||
propertiesCreatedRemoved = True
|
||||
return propertiesCreatedRemoved
|
||||
|
||||
|
||||
class OpsDefaultEditor:
|
||||
"""Class to collect and display default property editors for all registered operations.
|
||||
If a form is given at creation time it will integrate with that form and provide an interface to switch
|
||||
between the editors of different operations. If no form is provided the class assumes that the UI is
|
||||
taken care of somehow else and just serves as an interface to all operation editors."""
|
||||
|
||||
def __init__(self, obj, form):
|
||||
self.form = form
|
||||
self.obj = obj
|
||||
self.ops = sorted(
|
||||
[
|
||||
OpTaskPanel(self.obj, name, op)
|
||||
for name, op in PathUtil.keyValueIter(PathSetupSheet._RegisteredOps)
|
||||
],
|
||||
key=lambda op: op.name,
|
||||
)
|
||||
if form:
|
||||
parent = form.tabOpDefaults
|
||||
for op in self.ops:
|
||||
form.opDefaultOp.addItem(op.form.windowTitle(), op)
|
||||
op.form.setParent(parent)
|
||||
parent.layout().addWidget(op.form)
|
||||
op.form.hide()
|
||||
self.currentOp = None
|
||||
|
||||
def reject(self):
|
||||
pass
|
||||
|
||||
def accept(self):
|
||||
if any([op.accept() for op in self.ops]):
|
||||
Path.Log.track()
|
||||
|
||||
def getFields(self):
|
||||
pass
|
||||
|
||||
def updateUI(self):
|
||||
if self.currentOp:
|
||||
self.currentOp.form.hide()
|
||||
self.currentOp = None
|
||||
if self.form:
|
||||
current = self.form.opDefaultOp.currentIndex()
|
||||
if current < 0:
|
||||
current = 0
|
||||
self.currentOp = self.form.opDefaultOp.itemData(current)
|
||||
self.currentOp.form.show()
|
||||
|
||||
def updateModel(self, recomp=True):
|
||||
Path.Log.track()
|
||||
self.getFields()
|
||||
self.updateUI()
|
||||
if recomp:
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def setFields(self):
|
||||
self.updateUI()
|
||||
|
||||
def setupUi(self):
|
||||
for op in self.ops:
|
||||
op.setupUi()
|
||||
self.updateUI()
|
||||
if self.form:
|
||||
self.form.opDefaultOp.currentIndexChanged.connect(self.updateUI)
|
||||
|
||||
|
||||
class GlobalEditor(object):
|
||||
"""Editor for the global properties which affect almost every operation."""
|
||||
|
||||
def __init__(self, obj, form):
|
||||
self.form = form
|
||||
self.obj = obj
|
||||
|
||||
# initialized later
|
||||
self.clearanceHeightOffs = None
|
||||
self.safeHeightOffs = None
|
||||
self.rapidHorizontal = None
|
||||
self.rapidVertical = None
|
||||
|
||||
def reject(self):
|
||||
pass
|
||||
|
||||
def accept(self):
|
||||
self.getFields()
|
||||
|
||||
def getFields(self):
|
||||
def updateExpression(name, widget):
|
||||
value = str(widget.text())
|
||||
val = PathUtil.getProperty(self.obj, name)
|
||||
if val != value:
|
||||
PathUtil.setProperty(self.obj, name, value)
|
||||
|
||||
updateExpression("StartDepthExpression", self.form.setupStartDepthExpr)
|
||||
updateExpression("FinalDepthExpression", self.form.setupFinalDepthExpr)
|
||||
updateExpression("StepDownExpression", self.form.setupStepDownExpr)
|
||||
updateExpression(
|
||||
"ClearanceHeightExpression", self.form.setupClearanceHeightExpr
|
||||
)
|
||||
updateExpression("SafeHeightExpression", self.form.setupSafeHeightExpr)
|
||||
self.clearanceHeightOffs.updateProperty()
|
||||
self.safeHeightOffs.updateProperty()
|
||||
self.rapidVertical.updateProperty()
|
||||
self.rapidHorizontal.updateProperty()
|
||||
self.obj.CoolantMode = self.form.setupCoolantMode.currentText()
|
||||
|
||||
def selectInComboBox(self, name, combo):
|
||||
"""selectInComboBox(name, combo) ... helper function to select a specific value in a combo box."""
|
||||
index = combo.findText(name, QtCore.Qt.MatchFixedString)
|
||||
if index >= 0:
|
||||
combo.blockSignals(True)
|
||||
combo.setCurrentIndex(index)
|
||||
combo.blockSignals(False)
|
||||
|
||||
def updateUI(self):
|
||||
Path.Log.track()
|
||||
self.form.setupStartDepthExpr.setText(self.obj.StartDepthExpression)
|
||||
self.form.setupFinalDepthExpr.setText(self.obj.FinalDepthExpression)
|
||||
self.form.setupStepDownExpr.setText(self.obj.StepDownExpression)
|
||||
self.form.setupClearanceHeightExpr.setText(self.obj.ClearanceHeightExpression)
|
||||
self.form.setupSafeHeightExpr.setText(self.obj.SafeHeightExpression)
|
||||
self.clearanceHeightOffs.updateSpinBox()
|
||||
self.safeHeightOffs.updateSpinBox()
|
||||
self.rapidVertical.updateSpinBox()
|
||||
self.rapidHorizontal.updateSpinBox()
|
||||
self.selectInComboBox(self.obj.CoolantMode, self.form.setupCoolantMode)
|
||||
|
||||
def updateModel(self, recomp=True):
|
||||
Path.Log.track()
|
||||
self.getFields()
|
||||
self.updateUI()
|
||||
if recomp:
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def setFields(self):
|
||||
self.updateUI()
|
||||
|
||||
def setupUi(self):
|
||||
self.clearanceHeightOffs = PathGui.QuantitySpinBox(
|
||||
self.form.setupClearanceHeightOffs, self.obj, "ClearanceHeightOffset"
|
||||
)
|
||||
self.safeHeightOffs = PathGui.QuantitySpinBox(
|
||||
self.form.setupSafeHeightOffs, self.obj, "SafeHeightOffset"
|
||||
)
|
||||
self.rapidHorizontal = PathGui.QuantitySpinBox(
|
||||
self.form.setupRapidHorizontal, self.obj, "HorizRapid"
|
||||
)
|
||||
self.rapidVertical = PathGui.QuantitySpinBox(
|
||||
self.form.setupRapidVertical, self.obj, "VertRapid"
|
||||
)
|
||||
self.form.setupCoolantMode.addItems(self.obj.CoolantModes)
|
||||
self.setFields()
|
||||
|
||||
|
||||
class TaskPanel:
|
||||
"""TaskPanel for the SetupSheet - if it is being edited directly."""
|
||||
|
||||
def __init__(self, vobj):
|
||||
self.vobj = vobj
|
||||
self.obj = vobj.Object
|
||||
Path.Log.track(self.obj.Label)
|
||||
self.globalForm = FreeCADGui.PySideUic.loadUi(":/panels/SetupGlobal.ui")
|
||||
self.globalEditor = GlobalEditor(self.obj, self.globalForm)
|
||||
self.opsEditor = OpsDefaultEditor(self.obj, None)
|
||||
self.form = [op.form for op in self.opsEditor.ops] + [self.globalForm]
|
||||
FreeCAD.ActiveDocument.openTransaction("Edit SetupSheet")
|
||||
|
||||
def reject(self):
|
||||
self.globalEditor.reject()
|
||||
self.opsEditor.reject()
|
||||
FreeCAD.ActiveDocument.abortTransaction()
|
||||
FreeCADGui.Control.closeDialog()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def accept(self):
|
||||
self.globalEditor.accept()
|
||||
self.opsEditor.accept()
|
||||
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCADGui.ActiveDocument.resetEdit()
|
||||
FreeCADGui.Control.closeDialog()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def getFields(self):
|
||||
self.globalEditor.getFields()
|
||||
self.opsEditor.getFields()
|
||||
|
||||
def updateUI(self):
|
||||
self.globalEditor.updateUI()
|
||||
self.opsEditor.updateUI()
|
||||
|
||||
def updateModel(self):
|
||||
self.globalEditor.updateModel(False)
|
||||
self.opsEditor.updateModel(False)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def setFields(self):
|
||||
self.globalEditor.setFields()
|
||||
self.opsEditor.setFields()
|
||||
|
||||
def setupUi(self):
|
||||
self.globalEditor.setupUi()
|
||||
self.opsEditor.setupUi()
|
||||
|
||||
|
||||
def Create(name="SetupSheet"):
|
||||
"""Create(name='SetupSheet') ... creates a new setup sheet"""
|
||||
FreeCAD.ActiveDocument.openTransaction("Create Job")
|
||||
ssheet = PathSetupSheet.Create(name)
|
||||
PathIconViewProvider.Attach(ssheet, name)
|
||||
return ssheet
|
||||
|
||||
|
||||
PathIconViewProvider.RegisterViewProvider("SetupSheet", ViewProvider)
|
||||
@@ -1,219 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2018 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 Path
|
||||
|
||||
__title__ = "Setup Sheet for a Job."
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Prototype objects to allow extraction of setup sheet values and editing."
|
||||
|
||||
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
# Path.Log.trackModule(Path.Log.thisModule())
|
||||
|
||||
|
||||
class Property(object):
|
||||
"""Base class for all prototype properties"""
|
||||
|
||||
def __init__(self, name, propType, category, info):
|
||||
self.name = name
|
||||
self.propType = propType
|
||||
self.category = category
|
||||
self.info = info
|
||||
self.editorMode = 0
|
||||
self.value = None
|
||||
|
||||
def setValue(self, value):
|
||||
self.value = value
|
||||
|
||||
def getValue(self):
|
||||
return self.value
|
||||
|
||||
def setEditorMode(self, mode):
|
||||
self.editorMode = mode
|
||||
|
||||
def displayString(self):
|
||||
if self.value is None:
|
||||
t = self.typeString()
|
||||
p = "an" if t[0] in ["A", "E", "I", "O", "U"] else "a"
|
||||
return "%s %s" % (p, t)
|
||||
return self.value
|
||||
|
||||
def typeString(self):
|
||||
return "Property"
|
||||
|
||||
def setupProperty(self, obj, name, category, value):
|
||||
created = False
|
||||
if not hasattr(obj, name):
|
||||
PathLog.track('add', obj.Name, name, self.propType)
|
||||
obj.addProperty(self.propType, name, category, self.info)
|
||||
self.initProperty(obj, name)
|
||||
created = True
|
||||
PathLog.track('set', obj.Name, name, value, type(value))
|
||||
setattr(obj, name, value)
|
||||
return created
|
||||
|
||||
def initProperty(self, obj, name):
|
||||
pass
|
||||
|
||||
def setValueFromString(self, string):
|
||||
self.setValue(self.valueFromString(string))
|
||||
|
||||
def valueFromString(self, string):
|
||||
return string
|
||||
|
||||
|
||||
class PropertyEnumeration(Property):
|
||||
def typeString(self):
|
||||
return "Enumeration"
|
||||
|
||||
def setValue(self, value):
|
||||
if list == type(value):
|
||||
self.enums = value
|
||||
else:
|
||||
super(PropertyEnumeration, self).setValue(value)
|
||||
|
||||
def getEnumValues(self):
|
||||
return self.enums
|
||||
|
||||
def initProperty(self, obj, name):
|
||||
setattr(obj, name, self.enums)
|
||||
|
||||
|
||||
class PropertyQuantity(Property):
|
||||
def displayString(self):
|
||||
if self.value is None:
|
||||
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"
|
||||
|
||||
|
||||
class PropertyLength(PropertyQuantity):
|
||||
def typeString(self):
|
||||
return "Length"
|
||||
|
||||
|
||||
class PropertyFloat(Property):
|
||||
def typeString(self):
|
||||
return "Float"
|
||||
|
||||
def valueFromString(self, string):
|
||||
return float(string)
|
||||
|
||||
|
||||
class PropertyInteger(Property):
|
||||
def typeString(self):
|
||||
return "Integer"
|
||||
|
||||
def valueFromString(self, string):
|
||||
return int(string)
|
||||
|
||||
|
||||
class PropertyPercent(PropertyInteger):
|
||||
def typeString(self):
|
||||
return "Percent"
|
||||
|
||||
|
||||
class PropertyBool(Property):
|
||||
def typeString(self):
|
||||
return "Bool"
|
||||
|
||||
def valueFromString(self, string):
|
||||
return bool(string)
|
||||
|
||||
|
||||
class PropertyString(Property):
|
||||
def typeString(self):
|
||||
return "String"
|
||||
|
||||
|
||||
class PropertyMap(Property):
|
||||
def typeString(self):
|
||||
return "Map"
|
||||
|
||||
def displayString(self, value):
|
||||
return str(value)
|
||||
|
||||
|
||||
class OpPrototype(object):
|
||||
|
||||
PropertyType = {
|
||||
"App::PropertyAngle": PropertyAngle,
|
||||
"App::PropertyBool": PropertyBool,
|
||||
"App::PropertyDistance": PropertyDistance,
|
||||
"App::PropertyEnumeration": PropertyEnumeration,
|
||||
"App::PropertyFile": PropertyString,
|
||||
"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::PropertyMap": PropertyMap,
|
||||
"App::PropertyPercent": PropertyPercent,
|
||||
"App::PropertyString": PropertyString,
|
||||
"App::PropertyStringList": Property,
|
||||
"App::PropertyVectorDistance": Property,
|
||||
"App::PropertyVectorList": Property,
|
||||
"Part::PropertyPartShape": Property,
|
||||
"App::PropertyPythonObject": PropertyString,
|
||||
}
|
||||
|
||||
def __init__(self, name):
|
||||
self.Label = name
|
||||
self.properties = {}
|
||||
self.DoNotSetDefaultValues = True
|
||||
self.Proxy = None
|
||||
|
||||
def __setattr__(self, name, val):
|
||||
if name in ["Label", "DoNotSetDefaultValues", "properties", "Proxy"]:
|
||||
if name == "Proxy":
|
||||
val = None # make sure the proxy is never set
|
||||
return super(OpPrototype, self).__setattr__(name, val)
|
||||
self.properties[name].setValue(val)
|
||||
|
||||
def addProperty(self, typeString, name, category, info=None):
|
||||
prop = self.PropertyType[typeString](name, typeString, category, info)
|
||||
self.properties[name] = prop
|
||||
return self
|
||||
|
||||
def setEditorMode(self, name, mode):
|
||||
self.properties[name].setEditorMode(mode)
|
||||
|
||||
def getProperty(self, name):
|
||||
return self.properties[name]
|
||||
|
||||
def setupProperties(self, setup):
|
||||
return [p for p in self.properties if p.name in setup]
|
||||
@@ -1,229 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ***************************************************************************
|
||||
# * Copyright (c) 2018 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 Path
|
||||
import PathScripts.PathSetupSheetOpPrototype as PathSetupSheetOpPrototype
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
__title__ = "Setup Sheet Editor"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
__doc__ = "Task panel editor for a SetupSheet"
|
||||
|
||||
|
||||
LOGLEVEL = False
|
||||
|
||||
if LOGLEVEL:
|
||||
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
||||
Path.Log.trackModule(Path.Log.thisModule())
|
||||
else:
|
||||
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
||||
|
||||
|
||||
class _PropertyEditor(object):
|
||||
"""Base class of all property editors - just outlines the TableView delegate interface."""
|
||||
|
||||
def __init__(self, prop):
|
||||
self.prop = prop
|
||||
|
||||
def widget(self, parent):
|
||||
"""widget(parent) ... called by the delegate to get a new editor widget.
|
||||
Must be implemented by subclasses and return the widget."""
|
||||
pass
|
||||
|
||||
def setEditorData(self, widget):
|
||||
"""setEditorData(widget) ... called by the delegate to initialize the editor.
|
||||
The widget is the object returned by widget().
|
||||
Must be implemented by subclasses."""
|
||||
pass
|
||||
|
||||
def setModelData(self, widget):
|
||||
"""setModelData(widget) ... called by the delegate to store new values.
|
||||
Must be implemented by subclasses."""
|
||||
pass
|
||||
|
||||
|
||||
class _PropertyEnumEditor(_PropertyEditor):
|
||||
"""Editor for enumeration values - uses a combo box."""
|
||||
|
||||
def widget(self, parent):
|
||||
Path.Log.track(self.prop.name, self.prop.getEnumValues())
|
||||
return QtGui.QComboBox(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
widget.clear()
|
||||
widget.addItems(self.prop.getEnumValues())
|
||||
if self.prop.getValue():
|
||||
index = widget.findText(self.prop.getValue(), QtCore.Qt.MatchFixedString)
|
||||
if index >= 0:
|
||||
widget.setCurrentIndex(index)
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.prop.setValue(widget.currentText())
|
||||
|
||||
|
||||
class _PropertyBoolEditor(_PropertyEditor):
|
||||
"""Editor for boolean values - uses a combo box."""
|
||||
|
||||
def widget(self, parent):
|
||||
return QtGui.QComboBox(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
widget.clear()
|
||||
widget.addItems(["false", "true"])
|
||||
if not self.prop.getValue() is None:
|
||||
index = 1 if self.prop.getValue() else 0
|
||||
widget.setCurrentIndex(index)
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.prop.setValue(widget.currentText() == "true")
|
||||
|
||||
|
||||
class _PropertyStringEditor(_PropertyEditor):
|
||||
"""Editor for string values - uses a line edit."""
|
||||
|
||||
def widget(self, parent):
|
||||
return QtGui.QLineEdit(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
text = "" if self.prop.getValue() is None else self.prop.getValue()
|
||||
widget.setText(text)
|
||||
|
||||
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."""
|
||||
|
||||
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.Length)
|
||||
widget.setText(quantity.getUserPreferred()[0])
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.prop.setValue(FreeCAD.Units.Quantity(widget.text()))
|
||||
|
||||
|
||||
class _PropertyPercentEditor(_PropertyEditor):
|
||||
"""Editor for percent values - uses a spin box."""
|
||||
|
||||
def widget(self, parent):
|
||||
return QtGui.QSpinBox(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
widget.setRange(0, 100)
|
||||
value = self.prop.getValue()
|
||||
if value is None:
|
||||
value = 0
|
||||
widget.setValue(value)
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.prop.setValue(widget.value())
|
||||
|
||||
|
||||
class _PropertyIntegerEditor(_PropertyEditor):
|
||||
"""Editor for integer values - uses a spin box."""
|
||||
|
||||
def widget(self, parent):
|
||||
return QtGui.QSpinBox(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
value = self.prop.getValue()
|
||||
if value is None:
|
||||
value = 0
|
||||
widget.setValue(value)
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.prop.setValue(widget.value())
|
||||
|
||||
|
||||
class _PropertyFloatEditor(_PropertyEditor):
|
||||
"""Editor for float values - uses a double spin box."""
|
||||
|
||||
def widget(self, parent):
|
||||
return QtGui.QDoubleSpinBox(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
value = self.prop.getValue()
|
||||
if value is None:
|
||||
value = 0.0
|
||||
widget.setValue(value)
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.prop.setValue(widget.value())
|
||||
|
||||
|
||||
class _PropertyFileEditor(_PropertyEditor):
|
||||
def widget(self, parent):
|
||||
return QtGui.QLineEdit(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
text = "" if self.prop.getValue() is None else self.prop.getValue()
|
||||
widget.setText(text)
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.prop.setValue(widget.text())
|
||||
|
||||
|
||||
_EditorFactory = {
|
||||
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):
|
||||
"""Returns an editor class to be used for the given property."""
|
||||
factory = _EditorFactory[prop.__class__]
|
||||
if factory:
|
||||
return factory(prop)
|
||||
return None
|
||||
@@ -24,7 +24,7 @@ import FreeCAD
|
||||
import Path
|
||||
import Path.Base.Util as PathUtil
|
||||
import Path.Dressup.Utils as PathDressup
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathGui
|
||||
import PathScripts.PathJob as PathJob
|
||||
import PathSimulator
|
||||
import math
|
||||
|
||||
@@ -338,9 +338,9 @@ def SetupStockObject(obj, stockType):
|
||||
obj.StockType = stockType
|
||||
obj.setEditorMode("StockType", 2) # hide
|
||||
|
||||
import PathScripts.PathIconViewProvider
|
||||
import Path.Base.Gui.IconViewProvider
|
||||
|
||||
PathScripts.PathIconViewProvider.ViewProvider(obj.ViewObject, "Stock")
|
||||
Path.Base.Gui.IconViewProvider.ViewProvider(obj.ViewObject, "Stock")
|
||||
obj.ViewObject.Transparency = 90
|
||||
obj.ViewObject.DisplayMode = "Wireframe"
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import FreeCADGui
|
||||
import FreeCAD
|
||||
import Path
|
||||
import Path.Tool.Controller as PathToolController
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathGui
|
||||
import PathScripts.PathJobCmd as PathJobCmd
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
from PySide import QtGui
|
||||
|
||||
Reference in New Issue
Block a user