Files
FreeCAD/src/Mod/Path/PathScripts/PathOpGui.py
T

738 lines
27 KiB
Python

# -*- 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 PathScripts.PathGetPoint as PathGetPoint
import PathScripts.PathLog as PathLog
import PathScripts.PathSelection as PathSelection
import PathScripts.PathOp as PathOp
import importlib
from PathScripts import PathUtils
from PySide import QtCore, QtGui
# TaskPanelLayout
# 0 ... existing toolbox layout
# 1 ... reverse order
# 2 ... multi panel layout
# 3 ... multi panel layout reverse
TaskPanelLayout = 0
if False:
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
PathLog.trackModule(PathLog.thisModule())
else:
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
class ViewProvider(object):
def __init__(self, vobj, resources):
PathLog.track()
vobj.Proxy = self
self.deleteOnReject = True
self.OpIcon = ":/icons/%s.svg" % resources.pixmap
self.OpName = resources.name
self.OpPageModule = resources.opPageClass.__module__
self.OpPageClass = resources.opPageClass.__name__
def attach(self, vobj):
PathLog.track()
self.Object = vobj.Object
self.panel = None
return
def deleteObjectsOnReject(self):
PathLog.track()
return hasattr(self, 'deleteOnReject') and self.deleteOnReject
def setEdit(self, vobj, mode=0):
PathLog.track()
page = self.getTaskPanelOpPage(vobj.Object)
selection = self.getSelectionFactory()
self.setupTaskPanel(TaskPanel(vobj.Object, self.deleteObjectsOnReject(), page, selection))
self.deleteOnReject = False
return True
def setupTaskPanel(self, panel):
self.panel = panel
FreeCADGui.Control.closeDialog()
FreeCADGui.Control.showDialog(panel)
panel.setupUi()
def clearTaskPanel(self):
self.panel = None
def __getstate__(self):
PathLog.track()
state = {}
state['OpName'] = self.OpName
state['OpIcon'] = self.OpIcon
state['OpPageModule'] = self.OpPageModule
state['OpPageClass'] = self.OpPageClass
return state
def __setstate__(self, state):
self.OpName = state['OpName']
self.OpIcon = state['OpIcon']
self.OpPageModule = state['OpPageModule']
self.OpPageClass = state['OpPageClass']
def getIcon(self):
return self.OpIcon
def getTaskPanelOpPage(self, obj):
mod = importlib.import_module(self.OpPageModule)
cls = getattr(mod, self.OpPageClass)
return cls(obj, 0)
def getSelectionFactory(self):
return PathSelection.select(self.OpName)
def updateData(self, obj, prop):
# PathLog.track(obj.Label, prop) # Creates a lot of noise
if self.panel:
self.panel.updateData(obj, prop)
class TaskPanelPage(object):
# task panel interaction framework
def __init__(self, obj, features):
self.obj = obj
self.form = self.getForm()
self.setDirty()
self.setTitle('-')
self.features = features
def setDirty(self):
self.isdirty = True
def setClean(self):
self.isdirty = False
def pageGetFields(self):
self.getFields(self.obj)
self.setDirty()
def pageSetFields(self):
self.setFields(self.obj)
def pageRegisterSignalHandlers(self):
for signal in self.getSignalsForUpdate(self.obj):
signal.connect(self.pageGetFields)
self.registerSignalHandlers(self.obj)
def pageUpdateData(self, obj, prop):
self.updateData(obj, prop)
def setTitle(self, title):
self.title = title
def getTitle(self, obj):
return self.title
# subclass interface
def initPage(self, obj):
pass
def modifyStandardButtons(self, buttonBox):
pass
def getForm(self):
pass
def getFields(self, obj):
pass
def setFields(self, obj):
pass
def getSignalsForUpdate(self, obj):
return []
def registerSignalHandlers(self, obj):
pass
def updateData(self, obj, prop):
pass
# helpers
def selectInComboBox(self, name, combo):
index = combo.findText(name, QtCore.Qt.MatchFixedString)
if index >= 0:
combo.blockSignals(True)
combo.setCurrentIndex(index)
combo.blockSignals(False)
def setupToolController(self, obj, combo):
controllers = PathUtils.getToolControllers(self.obj)
labels = [c.Label for c in controllers]
combo.blockSignals(True)
combo.addItems(labels)
combo.blockSignals(False)
if obj.ToolController is None:
obj.ToolController = PathUtils.findToolController(obj)
if obj.ToolController is not None:
self.selectInComboBox(obj.ToolController.Label, combo)
def updateToolController(self, obj, combo):
tc = PathUtils.findToolController(obj, combo.currentText())
if obj.ToolController != tc:
obj.ToolController = tc
def updateInputField(self, obj, prop, widget):
value = FreeCAD.Units.Quantity(widget.text()).Value
if getattr(obj, prop) != value:
PathLog.debug("updateInputField(%s, %s): %.2f -> %.2f" % (obj.Label, prop, getattr(obj, prop), value))
setattr(obj, prop, value)
class TaskPanelBaseGeometryPage(TaskPanelPage):
DataObject = QtCore.Qt.ItemDataRole.UserRole
DataObjectSub = QtCore.Qt.ItemDataRole.UserRole + 1
def getForm(self):
return FreeCADGui.PySideUic.loadUi(":/panels/PageBaseGeometryEdit.ui")
def getTitle(self, obj):
return translate("PathOp", "Base Geometry")
def getFields(self, obj):
pass
def setFields(self, obj):
self.form.baseList.blockSignals(True)
self.form.baseList.clear()
for base in self.obj.Base:
for sub in base[1]:
item = QtGui.QListWidgetItem("%s.%s" % (base[0].Label, sub))
item.setData(self.DataObject, base[0])
item.setData(self.DataObjectSub, sub)
self.form.baseList.addItem(item)
self.form.baseList.blockSignals(False)
def itemActivated(self):
FreeCADGui.Selection.clearSelection()
for item in self.form.baseList.selectedItems():
obj = item.data(self.DataObject)
sub = item.data(self.DataObjectSub)
if sub:
FreeCADGui.Selection.addSelection(obj, sub)
else:
FreeCADGui.Selection.addSelection(obj)
#FreeCADGui.updateGui()
def supportsVertexes(self):
return self.features & PathOp.FeatureBaseVertexes
def supportsEdges(self):
return self.features & PathOp.FeatureBaseEdges
def supportsFaces(self):
return self.features & PathOp.FeatureBaseFaces
def supportsPanels(self):
return self.features & PathOp.FeatureBasePanels
def featureName(self):
if self.supportsEdges() and self.supportsFaces():
return 'features'
if self.supportsFaces():
return 'faces'
if self.supportsEdges():
return 'edges'
return 'nothing'
def addBaseGeometry(self, selection):
PathLog.track(selection)
if len(selection) != 1:
PathLog.error(translate("PathProject", "Please select %s from a single solid" % self.featureName()))
return False
sel = selection[0]
if sel.HasSubObjects:
if not self.supportsVertexes() and selection[0].SubObjects[0].ShapeType == "Vertex":
PathLog.error(translate("PathProject", "Vertexes are not supported"))
return False
if not self.supportsEdges() and selection[0].SubObjects[0].ShapeType == "Edge":
PathLog.error(translate("PathProject", "Edges are not supported"))
return False
if not self.supportsFaces() and selection[0].SubObjects[0].ShapeType == "Face":
PathLog.error(translate("PathProject", "Faces are not supported"))
return False
else:
if not self.supportsPanels() or not 'Panel' in sel.Object.Name:
PathLog.error(translate("PathProject", "Please select %s of a solid" % self.featureName()))
return False
for sub in sel.SubElementNames:
self.obj.Proxy.addBase(self.obj, sel.Object, sub)
return True
def addBase(self):
if self.addBaseGeometry(FreeCADGui.Selection.getSelectionEx()):
#self.obj.Proxy.execute(self.obj)
self.setFields(self.obj)
def deleteBase(self):
PathLog.track()
selected = self.form.baseList.selectedItems()
for item in selected:
self.form.baseList.takeItem(self.form.baseList.row(item))
self.updateBase()
#self.obj.Proxy.execute(self.obj)
#FreeCAD.ActiveDocument.recompute()
def updateBase(self):
newlist = []
for i in range(self.form.baseList.count()):
item = self.form.baseList.item(i)
obj = item.data(self.DataObject)
sub = str(item.data(self.DataObjectSub))
base = (obj, sub)
newlist.append(base)
PathLog.debug("Setting new base: %s -> %s" % (self.obj.Base, newlist))
self.obj.Base = newlist
#self.obj.Proxy.execute(self.obj)
#FreeCAD.ActiveDocument.recompute()
def registerSignalHandlers(self, obj):
self.form.baseList.itemSelectionChanged.connect(self.itemActivated)
self.form.addBase.clicked.connect(self.addBase)
self.form.deleteBase.clicked.connect(self.deleteBase)
self.form.updateBase.clicked.connect(self.updateBase)
def pageUpdateData(self, obj, prop):
if prop in ['Base']:
self.setFields(obj)
class TaskPanelBaseLocationPage(TaskPanelPage):
DataLocation = QtCore.Qt.ItemDataRole.UserRole
def getForm(self):
self.formLoc = FreeCADGui.PySideUic.loadUi(":/panels/PageBaseLocationEdit.ui")
self.formPts = FreeCADGui.PySideUic.loadUi(":/panels/PointEdit.ui")
self.formLoc.baseList.horizontalHeader().setResizeMode(QtGui.QHeaderView.Stretch)
self.formPts.setParent(self.formLoc.addRemoveEdit.parent())
self.formLoc.addRemoveEdit.parent().layout().addWidget(self.formPts)
self.formPts.hide()
self.getPoint = PathGetPoint.TaskPanel(self.formLoc.addRemoveEdit, self.formPts)
return self.formLoc
def modifyStandardButtons(self, buttonBox):
self.getPoint.buttonBox = buttonBox
def getTitle(self, obj):
return translate("PathOp", "Base Location")
def getFields(self, obj):
pass
def setFields(self, obj):
self.formLoc.baseList.blockSignals(True)
self.formLoc.baseList.clearContents()
self.formLoc.baseList.setRowCount(0)
for location in self.obj.Locations:
self.formLoc.baseList.insertRow(self.formLoc.baseList.rowCount())
item = QtGui.QTableWidgetItem("%.2f" % location.x)
item.setData(self.DataLocation, location.x)
self.formLoc.baseList.setItem(self.formLoc.baseList.rowCount()-1, 0, item)
item = QtGui.QTableWidgetItem("%.2f" % location.y)
item.setData(self.DataLocation, location.y)
self.formLoc.baseList.setItem(self.formLoc.baseList.rowCount()-1, 1, item)
self.formLoc.baseList.resizeColumnToContents(0)
self.formLoc.baseList.blockSignals(False)
self.itemActivated()
def removeLocation(self):
deletedRows = []
selected = self.formLoc.baseList.selectedItems()
for item in selected:
row = self.formLoc.baseList.row(item)
if not row in deletedRows:
deletedRows.append(row)
self.formLoc.baseList.removeRow(row)
self.updateLocations()
FreeCAD.ActiveDocument.recompute()
def updateLocations(self):
PathLog.track()
locations = []
for i in range(self.formLoc.baseList.rowCount()):
x = self.formLoc.baseList.item(i, 0).data(self.DataLocation)
y = self.formLoc.baseList.item(i, 1).data(self.DataLocation)
location = FreeCAD.Vector(x, y, 0)
locations.append(location)
self.obj.Locations = locations
def addLocation(self):
self.getPoint.getPoint(self.addLocationAt)
def addLocationAt(self, point, obj):
if point:
locations = self.obj.Locations
locations.append(point)
self.obj.Locations = locations
FreeCAD.ActiveDocument.recompute()
def editLocation(self):
selected = self.formLoc.baseList.selectedItems()
if selected:
row = self.formLoc.baseList.row(selected[0])
self.editRow = row
x = self.formLoc.baseList.item(row, 0).data(self.DataLocation)
y = self.formLoc.baseList.item(row, 1).data(self.DataLocation)
start = FreeCAD.Vector(x, y, 0)
self.getPoint.getPoint(self.editLocationAt, start)
def editLocationAt(self, point, obj):
if point:
self.formLoc.baseList.item(self.editRow, 0).setData(self.DataLocation, point.x)
self.formLoc.baseList.item(self.editRow, 1).setData(self.DataLocation, point.y)
self.updateLocations()
FreeCAD.ActiveDocument.recompute()
def itemActivated(self):
if self.formLoc.baseList.selectedItems():
self.form.removeLocation.setEnabled(True)
self.form.editLocation.setEnabled(True)
else:
self.form.removeLocation.setEnabled(False)
self.form.editLocation.setEnabled(False)
def registerSignalHandlers(self, obj):
self.form.baseList.itemSelectionChanged.connect(self.itemActivated)
self.formLoc.addLocation.clicked.connect(self.addLocation)
self.formLoc.removeLocation.clicked.connect(self.removeLocation)
self.formLoc.editLocation.clicked.connect(self.editLocation)
def pageUpdateData(self, obj, prop):
if prop in ['Locations']:
self.setFields(obj)
class TaskPanelHeightsPage(TaskPanelPage):
def getForm(self):
return FreeCADGui.PySideUic.loadUi(":/panels/PageHeightsEdit.ui")
def getTitle(self, obj):
return translate("Path_AreaOp", "Heights")
def getFields(self, obj):
self.updateInputField(obj, 'SafeHeight', self.form.safeHeight)
self.updateInputField(obj, 'ClearanceHeight', self.form.clearanceHeight)
def setFields(self, obj):
self.form.safeHeight.setText(FreeCAD.Units.Quantity(obj.SafeHeight.Value, FreeCAD.Units.Length).UserString)
self.form.clearanceHeight.setText(FreeCAD.Units.Quantity(obj.ClearanceHeight.Value, FreeCAD.Units.Length).UserString)
def getSignalsForUpdate(self, obj):
signals = []
signals.append(self.form.safeHeight.editingFinished)
signals.append(self.form.clearanceHeight.editingFinished)
return signals
def pageUpdateData(self, obj, prop):
if prop in ['SafeHeight', 'ClearanceHeight']:
self.setFields(obj)
class TaskPanelDepthsPage(TaskPanelPage):
def getForm(self):
return FreeCADGui.PySideUic.loadUi(":/panels/PageDepthsEdit.ui")
def initPage(self, obj):
if not PathOp.FeatureStepDown & self.features:
self.form.stepDown.hide()
self.form.stepDownLabel.hide()
if not PathOp.FeatureFinishDepth & self.features:
self.form.finishDepth.hide()
self.form.finishDepthLabel.hide()
def getTitle(self, obj):
return translate("PathOp", "Depths")
def getFields(self, obj):
self.updateInputField(obj, 'StartDepth', self.form.startDepth)
self.updateInputField(obj, 'FinalDepth', self.form.finalDepth)
if PathOp.FeatureStepDown & self.features:
self.updateInputField(obj, 'StepDown', self.form.stepDown)
if PathOp.FeatureFinishDepth & self.features:
self.updateInputField(obj, 'FinishDepth', self.form.finishDepth)
def setFields(self, obj):
self.form.startDepth.setText(FreeCAD.Units.Quantity(obj.StartDepth.Value, FreeCAD.Units.Length).UserString)
self.form.finalDepth.setText(FreeCAD.Units.Quantity(obj.FinalDepth.Value, FreeCAD.Units.Length).UserString)
if PathOp.FeatureStepDown & self.features:
self.form.stepDown.setText(FreeCAD.Units.Quantity(obj.StepDown.Value, FreeCAD.Units.Length).UserString)
if PathOp.FeatureFinishDepth & self.features:
self.form.finishDepth.setText(FreeCAD.Units.Quantity(obj.FinishDepth.Value, FreeCAD.Units.Length).UserString)
def getSignalsForUpdate(self, obj):
signals = []
signals.append(self.form.startDepth.editingFinished)
signals.append(self.form.finalDepth.editingFinished)
if PathOp.FeatureStepDown & self.features:
signals.append(self.form.stepDown.editingFinished)
if PathOp.FeatureFinishDepth & self.features:
signals.append(self.form.finishDepth.editingFinished)
return signals
def pageUpdateData(self, obj, prop):
if prop in ['StartDepth', 'FinalDepth', 'StepDown', 'FinishDepth']:
self.setFields(obj)
class TaskPanel(object):
def __init__(self, obj, deleteOnReject, opPage, selectionFactory):
PathLog.track(obj.Label, deleteOnReject, opPage, selectionFactory)
FreeCAD.ActiveDocument.openTransaction(translate("Path_AreaOp", "AreaOp Operation"))
self.deleteOnReject = deleteOnReject
self.featurePages = []
features = obj.Proxy.opFeatures(obj)
opPage.features = features
if PathOp.FeatureBaseGeometry & features:
if hasattr(opPage, 'taskPanelBaseGeometryPage'):
self.featurePages.append(opPage.taskPanelBaseGeometryPage(obj, features))
else:
self.featurePages.append(TaskPanelBaseGeometryPage(obj, features))
if PathOp.FeatureLocations & features:
if hasattr(opPage, 'taskPanelBaseLocationPage'):
self.featurePages.append(opPage.taskPanelBaseLocationPage(obj, features))
else:
self.featurePages.append(TaskPanelBaseLocationPage(obj, features))
if PathOp.FeatureDepths & features:
if hasattr(opPage, 'taskPanelDepthsPage'):
self.featurePages.append(opPage.taskPanelDepthsPage(obj, features))
else:
self.featurePages.append(TaskPanelDepthsPage(obj, features))
if PathOp.FeatureHeights & features:
if hasattr(opPage, 'taskPanelHeightsPage'):
self.featurePages.append(opPage.taskPanelHeightsPage(obj, features))
else:
self.featurePages.append(TaskPanelHeightsPage(obj, features))
opPage.setTitle(translate('PathOp', 'Operation'))
self.featurePages.append(opPage)
for page in self.featurePages:
page.initPage(obj)
if TaskPanelLayout < 2:
self.form = QtGui.QToolBox()
if TaskPanelLayout == 0:
for page in self.featurePages:
self.form.addItem(page.form, page.getTitle(obj))
self.form.setCurrentIndex(len(self.featurePages)-1)
else:
for page in reversed(self.featurePages):
self.form.addItem(page.form, page.getTitle(obj))
elif TaskPanelLayout == 2:
forms = []
for page in self.featurePages:
page.form.setWindowTitle(page.getTitle(obj))
forms.append(page.form)
self.form = forms
elif TaskPanelLayout == 3:
forms = []
for page in reversed(self.featurePages):
page.form.setWindowTitle(page.getTitle(obj))
forms.append(page.form)
self.form = forms
self.selectionFactory = selectionFactory
self.obj = obj
self.isdirty = True
def isDirty(self):
for page in self.featurePages:
if page.isdirty:
return True
return self.isdirty
def setClean(self):
self.isdirty = False
for page in self.featurePages:
page.setClean()
def accept(self):
FreeCAD.ActiveDocument.commitTransaction()
self.cleanup()
if self.isDirty:
self.panelGetFields()
FreeCAD.ActiveDocument.recompute()
def reject(self):
FreeCAD.ActiveDocument.abortTransaction()
self.cleanup()
if self.deleteOnReject:
FreeCAD.ActiveDocument.openTransaction(translate("Path_AreaOp", "Uncreate AreaOp Operation"))
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
def cleanup(self):
self.obj.ViewObject.Proxy.clearTaskPanel()
FreeCADGui.Control.closeDialog()
FreeCADGui.ActiveDocument.resetEdit()
FreeCADGui.Selection.removeObserver(self.s)
def clicked(self, button):
if button == QtGui.QDialogButtonBox.Apply:
self.panelGetFields()
self.setClean()
FreeCAD.ActiveDocument.recompute()
def modifyStandardButtons(self, buttonBox):
for page in self.featurePages:
page.modifyStandardButtons(buttonBox)
def panelGetFields(self):
PathLog.track()
for page in self.featurePages:
page.pageGetFields()
def panelSetFields(self):
PathLog.track()
for page in self.featurePages:
page.pageSetFields()
def open(self):
self.s = SelObserver(self.selectionFactory)
# install the function mode resident
FreeCADGui.Selection.addObserver(self.s)
def getStandardButtons(self):
return int(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Cancel)
def setupUi(self):
PathLog.track(self.deleteOnReject)
if self.deleteOnReject and PathOp.FeatureBaseGeometry & self.obj.Proxy.opFeatures(self.obj):
sel = FreeCADGui.Selection.getSelectionEx()
if len(sel) == 1 and sel[0].Object != self.obj:
for page in self.featurePages:
if hasattr(page, 'addBase'):
page.addBaseGeometry(sel)
self.panelSetFields()
for page in self.featurePages:
page.pageRegisterSignalHandlers()
def updateData(self, obj, prop):
# PathLog.track(obj.Label, prop) # creates a lot of noise
for page in self.featurePages:
page.pageUpdateData(obj, prop)
def needsFullSpace(self):
return True
class SelObserver:
def __init__(self, factory):
factory()
def __del__(self):
PathSelection.clear()
def addSelection(self, doc, obj, sub, pnt):
FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj + ')')
FreeCADGui.updateGui()
class _CommandSetStartPoint:
def GetResources(self):
return {'Pixmap': 'Path-StartPoint',
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_AreaOp", "Pick Start Point"),
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_AreaOp", "Pick Start Point")}
def IsActive(self):
if FreeCAD.ActiveDocument is None:
return False
sel = FreeCADGui.Selection.getSelection()
if not sel:
return False
obj = sel[0]
return obj and hasattr(obj, 'StartPoint')
def setpoint(self, point, o):
obj = FreeCADGui.Selection.getSelection()[0]
obj.StartPoint.x = point.x
obj.StartPoint.y = point.y
obj.StartPoint.z = obj.ClearanceHeight.Value
def Activated(self):
FreeCADGui.Snapper.getPoint(callback=self.setpoint)
def Create(res):
FreeCAD.ActiveDocument.openTransaction("Create %s" % res.name)
obj = res.objFactory(res.name)
vobj = ViewProvider(obj.ViewObject, res)
FreeCAD.ActiveDocument.commitTransaction()
obj.ViewObject.startEditing()
return obj
class CommandPathOp:
def __init__(self, resources):
self.res = resources
def GetResources(self):
ress = {'Pixmap': self.res.pixmap,
'MenuText': self.res.menuText,
'ToolTip': self.res.toolTip}
if self.res.accelKey:
ress['Accel'] = self.res.accelKey
return ress
def IsActive(self):
if FreeCAD.ActiveDocument is not None:
for o in FreeCAD.ActiveDocument.Objects:
if o.Name[:3] == "Job":
return True
return False
def Activated(self):
return Create(self.res)
class CommandResources:
def __init__(self, name, objFactory, opPageClass, pixmap, menuText, accelKey, toolTip):
self.name = name
self.objFactory = objFactory
self.opPageClass = opPageClass
self.pixmap = pixmap
self.menuText = menuText
self.accelKey = accelKey
self.toolTip = toolTip
def SetupOperation(name,
objFactory,
opPageClass,
pixmap,
menuText,
toolTip,
accelKey = None):
res = CommandResources(name, objFactory, opPageClass, pixmap, menuText, accelKey, toolTip)
FreeCADGui.addCommand("Path_%s" % name.replace(' ', '_'), CommandPathOp(res))
FreeCADGui.addCommand('Set_StartPoint', _CommandSetStartPoint())
FreeCAD.Console.PrintLog("Loading PathOpGui... done\n")