Added docstrings to Gui classes.

This commit is contained in:
Markus Lampert
2017-08-14 22:47:37 -07:00
committed by Yorik van Havre
parent 79337b998d
commit bc6ff3690e
13 changed files with 304 additions and 36 deletions

View File

@@ -29,6 +29,11 @@ import PathScripts.PathOpGui as PathOpGui
from PySide import QtCore, QtGui
__title__ = "Base for Circular Hole based operations' UI"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Implementation of circular hole specific base geometry page controller."
if True:
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
PathLog.trackModule(PathLog.thisModule())
@@ -36,14 +41,21 @@ else:
PathLog.setLevel(PathLog.Level.NOTICE, PathLog.thisModule())
class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage):
'''Controller class to be used for the BaseGeomtery page.
Circular holes don't just disply the feature, they also add a column
displaying the radius the feature describes. This page provides that
UI and functionality for all circular hole based operations.'''
DataFeatureName = QtCore.Qt.ItemDataRole.UserRole
DataObject = QtCore.Qt.ItemDataRole.UserRole + 1
DataObjectSub = QtCore.Qt.ItemDataRole.UserRole + 2
def getForm(self):
'''getForm() ... load and return page'''
return FreeCADGui.PySideUic.loadUi(":/panels/PageBaseHoleGeometryEdit.ui")
def setFields(self, obj):
'''setFields(obj) ... fill form with values from obj'''
PathLog.track()
self.form.baseList.blockSignals(True)
self.form.baseList.clearContents()
@@ -75,6 +87,7 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage):
self.form.baseList.blockSignals(False)
def itemActivated(self):
'''itemActivated() ... callback when item in table is selected'''
PathLog.track()
FreeCADGui.Selection.clearSelection()
activatedRows = []
@@ -92,6 +105,7 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage):
#FreeCADGui.updateGui()
def deleteBase(self):
'''deleteBase() ... callback for push button'''
PathLog.track()
deletedRows = []
selected = self.form.baseList.selectedItems()
@@ -105,6 +119,7 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage):
FreeCAD.ActiveDocument.recompute()
def updateBase(self):
'''updateBase() ... helper function to transfer current table to obj'''
PathLog.track()
newlist = []
for i in range(self.form.baseList.rowCount()):
@@ -118,6 +133,7 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage):
self.obj.Base = newlist
def checkedChanged(self):
'''checkeChanged() ... callback when checked status of a base feature changed'''
PathLog.track()
disabled = []
for i in xrange(0, self.form.baseList.rowCount()):
@@ -128,6 +144,7 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage):
FreeCAD.ActiveDocument.recompute()
def registerSignalHandlers(self, obj):
'''registerSignalHandlers(obj) ... setup signal handlers'''
self.form.baseList.itemSelectionChanged.connect(self.itemActivated)
self.form.addBase.clicked.connect(self.addBase)
self.form.deleteBase.clicked.connect(self.deleteBase)
@@ -135,6 +152,7 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage):
self.form.baseList.itemChanged.connect(self.checkedChanged)
def resetBase(self):
'''resetBase() ... push button callback'''
self.obj.Base = []
self.obj.Disabled = []
@@ -142,11 +160,14 @@ class TaskPanelHoleGeometryPage(PathOpGui.TaskPanelBaseGeometryPage):
FreeCAD.ActiveDocument.recompute()
def updateData(self, obj, prop):
'''updateData(obj, prop) ... callback whenever a property of the model changed'''
if prop in ['Base', 'Disabled']:
self.setFields(obj)
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
'''Base class for circular hole based operation's page controller.'''
def taskPanelBaseGeometryPage(self, obj, features):
'''taskPanelBaseGeometryPage(obj, features) ... Return circular hole specific page controller for Base Geometry.'''
return TaskPanelHoleGeometryPage(obj, features)

View File

@@ -31,6 +31,11 @@ import PathScripts.PathOpGui as PathOpGui
from PySide import QtCore, QtGui
__title__ = "Path Drilling Operation UI."
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "UI and Command for Path Drilling Operation."
if True:
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
PathLog.trackModule(PathLog.thisModule())
@@ -39,11 +44,14 @@ else:
class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
'''Controller for the drilling operation's page'''
def getForm(self):
'''getForm() ... return UI'''
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpDrillingEdit.ui")
def getFields(self, obj):
'''setFields(obj) ... update obj's properties with values from the UI'''
PathLog.track()
self.updateInputField(obj, 'PeckDepth', self.form.peckDepth)
self.updateInputField(obj, 'RetractHeight', self.form.retractHeight)
@@ -59,6 +67,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
self.updateToolController(obj, self.form.toolController)
def setFields(self, obj):
'''setFields(obj) ... update UI with obj properties' values'''
PathLog.track()
self.form.peckDepth.setText(FreeCAD.Units.Quantity(obj.PeckDepth.Value, FreeCAD.Units.Length).UserString)
@@ -83,6 +92,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
self.setupToolController(obj, self.form.toolController)
def getSignalsForUpdate(self, obj):
'''getSignalsForUpdate(obj) ... return list of signals which cause the receiver to update the model'''
signals = []
signals.append(self.form.retractHeight.editingFinished)
@@ -95,7 +105,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
return signals
PathOpGui.SetupOperation('Drilling',
Command = PathOpGui.SetupOperation('Drilling',
PathDrilling.Create,
TaskPanelOpPage,
'Path-Drilling',

View File

@@ -31,33 +31,40 @@ import PathScripts.PathSelection as PathSelection
from PySide import QtCore, QtGui
__title__ = "Path Engrave Operation UI"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Engrave operation page controller and command implementation."
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
FeaturePocket = 0x01
FeatureFacing = 0x02
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
'''Page controller class for the Engrave operation.'''
def getForm(self):
'''getForm() ... returns UI'''
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpEngraveEdit.ui")
def getFields(self, obj):
'''getFields(obj) ... transfers values from UI to obj's proprties'''
if obj.StartVertex != self.form.startVertex.value():
obj.StartVertex = self.form.startVertex.value()
self.updateToolController(obj, self.form.toolController)
def setFields(self, obj):
'''setFields(obj) ... transfers obj's property values to UI'''
self.form.startVertex.setValue(obj.StartVertex)
self.setupToolController(obj, self.form.toolController)
def getSignalsForUpdate(self, obj):
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
signals = []
signals.append(self.form.startVertex.editingFinished)
signals.append(self.form.toolController.currentIndexChanged)
return signals
PathOpGui.SetupOperation('Engrave',
Command = PathOpGui.SetupOperation('Engrave',
PathEngrave.Create,
TaskPanelOpPage,
'Path-Engrave',

View File

@@ -29,16 +29,32 @@ import FreeCADGui
from PySide import QtCore, QtGui
from pivy import coin
class TaskPanel:
__title__ = "Path GetPoint UI"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Helper class to use FreeCADGUi.Snapper to let the user enter arbitray points while the task panel is active."
def __init__(self, formOrig, formPoint):
self.formOrig = formOrig
self.formPoint = formPoint
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):
'''__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
def setupUi(self):
'''setupUi() ... internal function - do not call.'''
self.formPoint.buttonBox.accepted.connect(self.pointAccept)
self.formPoint.buttonBox.rejected.connect(self.pointReject)
@@ -47,6 +63,7 @@ class TaskPanel:
self.formPoint.ifValueZ.editingFinished.connect(self.updatePoint)
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)
@@ -56,11 +73,19 @@ class TaskPanel:
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.'''
def displayPoint(p):
self.formPoint.ifValueX.setText(FreeCAD.Units.Quantity(p.x, FreeCAD.Units.Length).UserString)
@@ -111,6 +136,7 @@ class TaskPanel:
FreeCADGui.Snapper.forceGridOff=True
def pointFinish(self, ok, cleanup = True):
'''pointFinish(ok, cleanup=True) ... internal function - do not call.'''
obj = FreeCADGui.Snapper.lastSnappedObject
if cleanup:
@@ -129,18 +155,23 @@ class TaskPanel:
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)
@@ -151,6 +182,7 @@ class TaskPanel:
self.view = None
def updatePoint(self):
'''updatePoint() ... internal function - do not call.'''
x = FreeCAD.Units.Quantity(self.formPoint.ifValueX.text()).Value
y = FreeCAD.Units.Quantity(self.formPoint.ifValueY.text()).Value
z = FreeCAD.Units.Quantity(self.formPoint.ifValueZ.text()).Value

View File

@@ -31,6 +31,8 @@ import PathScripts.PathOpGui as PathOpGui
from PySide import QtCore, QtGui
__doc__ = "Helix operation page controller and command implementation."
if True:
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
PathLog.trackModule(PathLog.thisModule())
@@ -39,11 +41,14 @@ else:
class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
'''Page controller class for Helix operations.'''
def getForm(self):
'''getForm() ... return UI'''
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpHelixEdit.ui")
def getFields(self, obj):
'''getFields(obj) ... transfers values from UI to obj's proprties'''
PathLog.track()
if obj.Direction != str(self.form.direction.currentText()):
obj.Direction = str(self.form.direction.currentText())
@@ -55,6 +60,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
self.updateToolController(obj, self.form.toolController)
def setFields(self, obj):
'''setFields(obj) ... transfers obj's property values to UI'''
PathLog.track()
self.form.stepOverPercent.setValue(obj.StepOver)
@@ -64,6 +70,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
self.setupToolController(obj, self.form.toolController)
def getSignalsForUpdate(self, obj):
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
signals = []
signals.append(self.form.stepOverPercent.editingFinished)
@@ -73,7 +80,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
return signals
PathOpGui.SetupOperation('Helix',
Command = PathOpGui.SetupOperation('Helix',
PathHelix.Create,
TaskPanelOpPage,
'Path-Helix',

View File

@@ -30,12 +30,19 @@ import PathScripts.PathPocketBaseGui as PathPocketBaseGui
from PySide import QtCore
__title__ = "Path Face Mill Operation UI"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Face Mill operation page controller and command implementation."
class TaskPanelOpPage(PathPocketBaseGui.TaskPanelOpPage):
'''Page controller class for the face milling operation.'''
def pocketFeatures(self):
'''pocketFeatures() ... return FeatureFacing (see PathPocketBaseGui)'''
return PathPocketBaseGui.FeatureFacing
PathOpGui.SetupOperation('MillFace',
Command = PathOpGui.SetupOperation('MillFace',
PathMillFace.Create,
TaskPanelOpPage,
'Path-Face',

View File

@@ -33,6 +33,11 @@ import importlib
from PathScripts import PathUtils
from PySide import QtCore, QtGui
__title__ = "Path Operation UI base classes"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Base classes and framework for Path operation's UI"
# TaskPanelLayout
# 0 ... existing toolbox layout
# 1 ... reverse order
@@ -52,6 +57,11 @@ def translate(context, text, disambig=None):
class ViewProvider(object):
'''Generic view provider for path objects.
Deducts the icon name from operation name, brings up the TaskPanel
with pages corresponding to the operation's opFeatures() and forwards
property change notifications to the page controllers.
'''
def __init__(self, vobj, resources):
PathLog.track()
@@ -69,10 +79,15 @@ class ViewProvider(object):
return
def deleteObjectsOnReject(self):
'''deleteObjectsOnReject() ... return true if all objects should
be created if the user hits cancel. This is used during the initial
edit session, if the user does not press OK, it is assumed they've
changed their mind about creating the operation.'''
PathLog.track()
return hasattr(self, 'deleteOnReject') and self.deleteOnReject
def setEdit(self, vobj, mode=0):
'''setEdit(vobj, mode=0) ... initiate editing of receivers model.'''
PathLog.track()
page = self.getTaskPanelOpPage(vobj.Object)
selection = self.getSelectionFactory()
@@ -81,15 +96,19 @@ class ViewProvider(object):
return True
def setupTaskPanel(self, panel):
'''setupTaskPanel(panel) ... internal function to start the editor.'''
self.panel = panel
FreeCADGui.Control.closeDialog()
FreeCADGui.Control.showDialog(panel)
panel.setupUi()
def clearTaskPanel(self):
'''clearTaskPanel() ... internal callback function when editing has finished.'''
self.panel = None
def __getstate__(self):
'''__getstate__() ... callback before receiver is saved to a file.
Returns a dictionary with the receiver's resources as strings.'''
PathLog.track()
state = {}
state['OpName'] = self.OpName
@@ -99,31 +118,41 @@ class ViewProvider(object):
return state
def __setstate__(self, state):
'''__setstate__(state) ... callback on restoring a saved instance, pendant to __getstate__()
state is the dictionary returned by __getstate__().'''
self.OpName = state['OpName']
self.OpIcon = state['OpIcon']
self.OpPageModule = state['OpPageModule']
self.OpPageClass = state['OpPageClass']
def getIcon(self):
'''getIcon() ... the icon used in the object tree'''
return self.OpIcon
def getTaskPanelOpPage(self, obj):
'''getTaskPanelOpPage(obj) ... use the stored information to instanciate the receiver op's page controller.'''
mod = importlib.import_module(self.OpPageModule)
cls = getattr(mod, self.OpPageClass)
return cls(obj, 0)
def getSelectionFactory(self):
'''getSelectionFactory() ... return a factory function that can be used to create the selection observer.'''
return PathSelection.select(self.OpName)
def updateData(self, obj, prop):
'''updateData(obj, prop) ... callback whenever a property of the receiver's model is assigned.
The callback is forwarded to the task panel - in case an editing session is ongoing.'''
# PathLog.track(obj.Label, prop) # Creates a lot of noise
if self.panel:
self.panel.updateData(obj, prop)
class TaskPanelPage(object):
'''Base class for all task panel pages.'''
# task panel interaction framework
def __init__(self, obj, features):
'''__init__(obj, features) ... framework initialisation.
Do not overwrite, implement initPage(obj) instead.'''
self.obj = obj
self.form = self.getForm()
self.setDirty()
@@ -131,50 +160,94 @@ class TaskPanelPage(object):
self.features = features
def setDirty(self):
'''setDirty() ... mark receiver as dirty, causing the model to be recalculated if OK or Apply is pressed.'''
self.isdirty = True
def setClean(self):
'''setClean() ... mark receiver as clean, indicating there is no need to recalculate the model even if the user presses OK or Apply.'''
self.isdirty = False
def pageGetFields(self):
'''pageGetFields() ... internal callback.
Do not overwrite, implement getFields(obj) instead.'''
self.getFields(self.obj)
self.setDirty()
def pageSetFields(self):
'''pageSetFields() ... internal callback.
Do not overwrite, implement setFields(obj) instead.'''
self.setFields(self.obj)
def pageRegisterSignalHandlers(self):
'''pageRegisterSignalHandlers() .. internal callback.
Registers a callback for all signals returned by getSignalsForUpdate(obj).
Do not overwrite, implement getSignalsForUpdate(obj) and/or registerSignalHandlers(obj) instead.'''
for signal in self.getSignalsForUpdate(self.obj):
signal.connect(self.pageGetFields)
self.registerSignalHandlers(self.obj)
def pageUpdateData(self, obj, prop):
'''pageUpdateData(obj, prop) ... internal callback.
Do not overwrite, implement updateData(obj) instaed.'''
self.updateData(obj, prop)
def setTitle(self, title):
'''setTitle(title) ... sets a title for the page.'''
self.title = title
def getTitle(self, obj):
'''getTitle(obj) ... return title to be used for the receiver page.
The default implementation returns what was previously set with setTitle(title).
Can safely be overwritten by subclasses.'''
return self.title
# subclass interface
def initPage(self, obj):
'''initPage(obj) ... overwrite to customize UI for specific model.
Note that this function is invoked after all page controllers have been created.
Should be overwritten by subclasses.'''
pass
def modifyStandardButtons(self, buttonBox):
'''modifyStandardButtons(buttonBox) ... overwrite if the task panel standard buttons need to be modified.
Can safely be overwritten by subclasses.'''
pass
def getForm(self):
'''getForm() ... return UI form for this page.
Must be overwritten by subclasses.'''
pass
def getFields(self, obj):
'''getFields(obj) ... overwrite to transfer values from UI to obj's properties.
Can safely be overwritten by subclasses.'''
pass
def setFields(self, obj):
'''setFields(obj) ... overwrite to transfer obj's property values to UI.
Can safely be overwritten by subclasses.'''
pass
def getSignalsForUpdate(self, obj):
'''getSignalsForUpdate(obj) ... return signals which, when triggered, cause the receiver to update the model.
See also registerSignalHandlers(obj)
Can safely be overwritten by subclasses.'''
return []
def registerSignalHandlers(self, obj):
'''registerSignalHandlers(obj) ... overwrite to register custom signal handlers.
In case an update of a model is not the desired operation of a signal invocation
(see getSignalsForUpdate(obj)) this function can be used to register signal handlers
manually.
Can safely be overwritten by subclasses.'''
pass
def updateData(self, obj, prop):
'''updateData(obj, prop) ... overwrite if the receiver needs to react to property changes that might not have been caused by the receiver itself.
Sometimes a model will recalculate properties based on a change of another property. In order to keep the UI up to date with such changes this
function can be used.
Please note that the callback is synchronous with the property assignment operation. Also note that the notification is invoked regardless of the
actual value of the property assignment. In other words it also fires if a property gets assigned the same value it already has.
Taking above observations into account the implementation has to take care that it doesn't overwrite modified UI values by invoking setFields(obj).
This can happen if a subclass unconditionally transfers all values in getFields(obj) to the model and just calls setFields(obj) in this callback.
In such a scenario the first property assignment will cause all changes in the UI of the other fields to be overwritten by setFields(obj).
You have been warned.'''
pass
# helpers
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)
@@ -182,6 +255,7 @@ class TaskPanelPage(object):
combo.blockSignals(False)
def setupToolController(self, obj, combo):
'''setupToolController(obj, combo) ... helper function to setup obj's ToolController in the given combo box.'''
controllers = PathUtils.getToolControllers(self.obj)
labels = [c.Label for c in controllers]
combo.blockSignals(True)
@@ -194,17 +268,20 @@ class TaskPanelPage(object):
self.selectInComboBox(obj.ToolController.Label, combo)
def updateToolController(self, obj, combo):
'''updateToolController(obj, combo) ... helper function to update obj's ToolController property if a different one has been selected in the combo box.'''
tc = PathUtils.findToolController(obj, combo.currentText())
if obj.ToolController != tc:
obj.ToolController = tc
def updateInputField(self, obj, prop, widget):
'''updateInputField(obj, prop, widget) ... helper function to update obj's property named prop with the value from widget, if it has changed.'''
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):
'''Page controller for the base geometry.'''
DataObject = QtCore.Qt.ItemDataRole.UserRole
DataObjectSub = QtCore.Qt.ItemDataRole.UserRole + 1
@@ -323,17 +400,14 @@ class TaskPanelBaseGeometryPage(TaskPanelPage):
class TaskPanelBaseLocationPage(TaskPanelPage):
'''Page controller for base locations. Uses PathGetPoint.'''
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)
self.getPoint = PathGetPoint.TaskPanel(self.formLoc.addRemoveEdit)
return self.formLoc
def modifyStandardButtons(self, buttonBox):
@@ -430,10 +504,11 @@ class TaskPanelBaseLocationPage(TaskPanelPage):
class TaskPanelHeightsPage(TaskPanelPage):
'''Page controller for heights.'''
def getForm(self):
return FreeCADGui.PySideUic.loadUi(":/panels/PageHeightsEdit.ui")
def getTitle(self, obj):
return translate("Path_AreaOp", "Heights")
return translate("Path", "Heights")
def getFields(self, obj):
self.updateInputField(obj, 'SafeHeight', self.form.safeHeight)
self.updateInputField(obj, 'ClearanceHeight', self.form.clearanceHeight)
@@ -452,10 +527,9 @@ class TaskPanelHeightsPage(TaskPanelPage):
class TaskPanelDepthsPage(TaskPanelPage):
'''Page controller for depths.'''
def getForm(self):
return FreeCADGui.PySideUic.loadUi(":/panels/PageDepthsEdit.ui")
def initPage(self, obj):
if not PathOp.FeatureStepDown & self.features:
self.form.stepDown.hide()
@@ -464,10 +538,8 @@ class TaskPanelDepthsPage(TaskPanelPage):
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)
@@ -491,16 +563,23 @@ class TaskPanelDepthsPage(TaskPanelPage):
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):
'''
Generic TaskPanel implementation handling the standard Path operation layout.
This class only implements the framework and takes care of bringing all pages up and down in a controller fashion.
It implements the standard editor behaviour for OK, Cancel and Apply and tracks if the model is still in sync with
the UI.
However, all display and processing of fields is handled by the page contollers which are managed in a list. All
event callbacks and framework actions are forwarded to the page controllers in turn and each page controller is
expected to process all events concerning the data it manages.
'''
def __init__(self, obj, deleteOnReject, opPage, selectionFactory):
PathLog.track(obj.Label, deleteOnReject, opPage, selectionFactory)
FreeCAD.ActiveDocument.openTransaction(translate("Path_AreaOp", "AreaOp Operation"))
FreeCAD.ActiveDocument.openTransaction(translate("Path", "AreaOp Operation"))
self.deleteOnReject = deleteOnReject
self.featurePages = []
@@ -564,17 +643,20 @@ class TaskPanel(object):
self.isdirty = True
def isDirty(self):
'''isDirty() ... returns true if the model is not in sync with the UI anymore.'''
for page in self.featurePages:
if page.isdirty:
return True
return self.isdirty
def setClean(self):
'''setClean() ... set the reciever and all its pages clean.'''
self.isdirty = False
for page in self.featurePages:
page.setClean()
def accept(self):
'''accept() ... callback invoked when user presses the task panel OK button.'''
FreeCAD.ActiveDocument.commitTransaction()
self.cleanup()
if self.isDirty:
@@ -582,49 +664,58 @@ class TaskPanel(object):
FreeCAD.ActiveDocument.recompute()
def reject(self):
'''reject() ... callback invoked when user presses the task panel Cancel button.'''
FreeCAD.ActiveDocument.abortTransaction()
self.cleanup()
if self.deleteOnReject:
FreeCAD.ActiveDocument.openTransaction(translate("Path_AreaOp", "Uncreate AreaOp Operation"))
FreeCAD.ActiveDocument.openTransaction(translate("Path", "Uncreate AreaOp Operation"))
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
FreeCAD.ActiveDocument.commitTransaction()
FreeCAD.ActiveDocument.recompute()
def cleanup(self):
'''cleanup() ... implements common cleanup tasks.'''
self.obj.ViewObject.Proxy.clearTaskPanel()
FreeCADGui.Control.closeDialog()
FreeCADGui.ActiveDocument.resetEdit()
FreeCADGui.Selection.removeObserver(self.s)
def clicked(self, button):
'''clicked(button) ... callback invoked when the user presses any of the task panel buttons.'''
if button == QtGui.QDialogButtonBox.Apply:
self.panelGetFields()
self.setClean()
FreeCAD.ActiveDocument.recompute()
def modifyStandardButtons(self, buttonBox):
'''modifyStandarButtons(buttonBox) ... callback in case the task panel buttons need to be modified.'''
for page in self.featurePages:
page.modifyStandardButtons(buttonBox)
def panelGetFields(self):
'''panelGetFields() ... invoked to trigger a complete transfer of UI data to the model.'''
PathLog.track()
for page in self.featurePages:
page.pageGetFields()
def panelSetFields(self):
'''panelSetFields() ... invoked to trigger a complete transfer of the model's propeties to the UI.'''
PathLog.track()
for page in self.featurePages:
page.pageSetFields()
def open(self):
'''open() ... callback invoked when the task panel is opened.'''
self.s = SelObserver(self.selectionFactory)
# install the function mode resident
FreeCADGui.Selection.addObserver(self.s)
def getStandardButtons(self):
'''getStandardButtons() ... returns the Buttons for the task panel.'''
return int(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Apply | QtGui.QDialogButtonBox.Cancel)
def setupUi(self):
'''setupUi() ... internal function to initialise all pages.'''
PathLog.track(self.deleteOnReject)
if self.deleteOnReject and PathOp.FeatureBaseGeometry & self.obj.Proxy.opFeatures(self.obj):
@@ -639,6 +730,7 @@ class TaskPanel(object):
page.pageRegisterSignalHandlers()
def updateData(self, obj, prop):
'''updateDate(obj, prop) ... callback invoked whenever a model's property is assigned a value.'''
# PathLog.track(obj.Label, prop) # creates a lot of noise
for page in self.featurePages:
page.pageUpdateData(obj, prop)
@@ -647,7 +739,8 @@ class TaskPanel(object):
return True
class SelObserver:
'''Implementation of the selection observer used by the task panel.
Its specific behaviour is determined by the factory function.'''
def __init__(self, factory):
factory()
@@ -658,11 +751,12 @@ class SelObserver:
FreeCADGui.doCommand('Gui.Selection.addSelection(FreeCAD.ActiveDocument.' + obj + ')')
FreeCADGui.updateGui()
class _CommandSetStartPoint:
class CommandSetStartPoint:
'''Command to set the start point for an operation.'''
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")}
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path", "Pick Start Point"),
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path", "Pick Start Point")}
def IsActive(self):
if FreeCAD.ActiveDocument is None:
@@ -683,6 +777,10 @@ class _CommandSetStartPoint:
FreeCADGui.Snapper.getPoint(callback=self.setpoint)
def Create(res):
'''Create(res) ... generic implementation of a create function.
res is an instance of CommandResources. It is not excpected that the user invokes
this function directly, but calls the Activated() function of the Command object
that is created in each operations Gui implementation.'''
FreeCAD.ActiveDocument.openTransaction("Create %s" % res.name)
obj = res.objFactory(res.name)
vobj = ViewProvider(obj.ViewObject, res)
@@ -692,6 +790,10 @@ def Create(res):
return obj
class CommandPathOp:
'''Generic, data driven implementation of a Path operation creation command.
Instances of this class are stored in all Path operation Gui modules and can
be used to create said operations with view providers and all.'''
def __init__(self, resources):
self.res = resources
@@ -714,6 +816,7 @@ class CommandPathOp:
return Create(self.res)
class CommandResources:
'''POD class to hold command specific resources.'''
def __init__(self, name, objFactory, opPageClass, pixmap, menuText, accelKey, toolTip):
self.name = name
self.objFactory = objFactory
@@ -730,11 +833,21 @@ def SetupOperation(name,
menuText,
toolTip,
accelKey = None):
'''SetupOperation(name, objFactory, opPageClass, pixmap, menuText, toolTip, accelKey=None)
Creates an instance of CommandPathOp with the given parameters and registers the command with FreeCAD.
When activated it creates a model with proxy (by invoking objFactory), assign a view provider to it
(see ViewProvider in this module) and starts the editor specif for theis operation (driven by opPageClass).
This is an internal function that is automatically called by the intialisation code for each operation.
It is not expected to be called manually.
'''
res = CommandResources(name, objFactory, opPageClass, pixmap, menuText, accelKey, toolTip)
FreeCADGui.addCommand("Path_%s" % name.replace(' ', '_'), CommandPathOp(res))
command = CommandPathOp(res)
FreeCADGui.addCommand("Path_%s" % name.replace(' ', '_'), command)
return command
FreeCADGui.addCommand('Set_StartPoint', _CommandSetStartPoint())
FreeCADGui.addCommand('Set_StartPoint', CommandSetStartPoint())
FreeCAD.Console.PrintLog("Loading PathOpGui... done\n")

View File

@@ -31,6 +31,11 @@ import PathScripts.PathSelection as PathSelection
from PySide import QtCore, QtGui
__title__ = "Path Pocket Base Operation UI"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Base page controller and command implementation for path pocket operations."
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
@@ -38,8 +43,21 @@ FeaturePocket = 0x01
FeatureFacing = 0x02
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
'''Page controller class for pocket operations, supports two different features:
FeaturePocket ... used for pocketing operation
FeatureFacing ... used for face milling operation
'''
def pocketFeatures(self):
'''pocketFeatures() ... return which features of the UI are supported by the operation.
Typically one of the following is enabled:
FeaturePocket ... used for pocketing operation
FeatureFacing ... used for face milling operation
Must be overwritten by subclasses'''
pass
def getForm(self):
'''getForm() ... returns UI, adapted to the resutls from pocketFeatures()'''
form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpPocketFullEdit.ui")
if not FeaturePocket & self.pocketFeatures():
@@ -51,6 +69,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
return form
def getFields(self, obj):
'''getFields(obj) ... transfers values from UI to obj's proprties'''
if obj.CutMode != str(self.form.cutMode.currentText()):
obj.CutMode = str(self.form.cutMode.currentText())
if obj.StepOver != self.form.stepOverPercent.value():
@@ -72,6 +91,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
obj.BoundaryShape = str(self.form.boundaryShape.currentText())
def setFields(self, obj):
'''setFields(obj) ... transfers obj's property values to UI'''
self.form.zigZagAngle.setText(FreeCAD.Units.Quantity(obj.ZigZagAngle, FreeCAD.Units.Angle).UserString)
self.form.stepOverPercent.setValue(obj.StepOver)
@@ -88,6 +108,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
self.selectInComboBox(obj.BoundaryShape, self.form.boundaryShape)
def getSignalsForUpdate(self, obj):
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
signals = []
signals.append(self.form.cutMode.currentIndexChanged)

View File

@@ -29,12 +29,19 @@ import PathScripts.PathPocketBaseGui as PathPocketBaseGui
from PySide import QtCore
__title__ = "Path Pocket Operation UI"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Pocket operation page controller and command implementation."
class TaskPanelOpPage(PathPocketBaseGui.TaskPanelOpPage):
'''Page controller class for Pocket operation'''
def pocketFeatures(self):
'''pocketFeatures() ... return FeaturePocket (see PathPocketBaseGui)'''
return PathPocketBaseGui.FeaturePocket
PathOpGui.SetupOperation('Pocket',
Command = PathOpGui.SetupOperation('Pocket',
PathPocket.Create,
TaskPanelOpPage,
'Path-Pocket',

View File

@@ -31,6 +31,11 @@ import PathScripts.PathSelection as PathSelection
from PySide import QtCore
__title__ = "Path Profile Operation Base UI"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Base page controller for profile operations."
def translate(context, text, disambig=None):
return QtCore.QCoreApplication.translate(context, text, disambig)
@@ -38,8 +43,21 @@ FeatureSide = 0x01
FeatureProcessing = 0x02
class TaskPanelOpPage(PathOpGui.TaskPanelPage):
'''Base class for profile operation page controllers. Two sub features are
support
FeatureSide ... Is the Side property exposed in the UI
FeatureProcessing ... Are the processing check boxes supported by the operation
'''
def profileFeatures(self):
'''profileFeatures() ... return which of the optional profile features are supported.
Currently two features are supported:
FeatureSide ... Is the Side property exposed in the UI
FeatureProcessing ... Are the processing check boxes supported by the operation
Must be overwritten by subclasses.'''
def getForm(self):
'''getForm() ... returns UI customized according to profileFeatures()'''
form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpProfileFullEdit.ui")
if not FeatureSide & self.profileFeatures():
@@ -54,6 +72,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
return form
def getFields(self, obj):
'''getFields(obj) ... transfers values from UI to obj's proprties'''
self.updateInputField(obj, 'OffsetExtra', self.form.extraOffset)
if obj.UseComp != self.form.useCompensation.isChecked():
obj.UseComp = self.form.useCompensation.isChecked()
@@ -77,6 +96,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
obj.processCircles = self.form.processCircles.isChecked()
def setFields(self, obj):
'''setFields(obj) ... transfers obj's property values to UI'''
self.form.extraOffset.setText(FreeCAD.Units.Quantity(obj.OffsetExtra.Value, FreeCAD.Units.Length).UserString)
self.form.useCompensation.setChecked(obj.UseComp)
self.form.useStartPoint.setChecked(obj.UseStartPoint)
@@ -93,6 +113,7 @@ class TaskPanelOpPage(PathOpGui.TaskPanelPage):
self.form.processCircles.setChecked(obj.processCircles)
def getSignalsForUpdate(self, obj):
'''getSignalsForUpdate(obj) ... return list of signals for updating obj'''
signals = []
signals.append(self.form.direction.currentIndexChanged)
signals.append(self.form.useCompensation.clicked)

View File

@@ -29,13 +29,19 @@ import PathScripts.PathProfileContour as PathProfileContour
from PySide import QtCore
__title__ = "Path Contour Operation UI"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Contour operation page controller and command implementation."
class TaskPanelOpPage(PathProfileBaseGui.TaskPanelOpPage):
'''Page controller for the contour operation UI.'''
def profileFeatures(self):
# I know this looks bad, contour is just the most basic profile op there is
'''profileFeatues() ... return 0 - profile doesn't support any of the optional UI features.'''
return 0
PathOpGui.SetupOperation('Contour',
Command = PathOpGui.SetupOperation('Contour',
PathProfileContour.Create,
TaskPanelOpPage,
'Path-Contour',

View File

@@ -29,12 +29,20 @@ import PathScripts.PathProfileEdges as PathProfileEdges
from PySide import QtCore
__title__ = "Path Profile based on edges Operation UI"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Profile based on edges operation page controller and command implementation."
class TaskPanelOpPage(PathProfileBaseGui.TaskPanelOpPage):
'''Page controller for profile based on edges operation.'''
def profileFeatures(self):
'''profileFeatures() ... return FeatureSide
See PathProfileBaseGui.py for details.'''
return PathProfileBaseGui.FeatureSide
PathOpGui.SetupOperation('Profile Edges',
Command = PathOpGui.SetupOperation('Profile Edges',
PathProfileEdges.Create,
TaskPanelOpPage,
'Path-Profile-Edges',

View File

@@ -29,12 +29,20 @@ import PathScripts.PathProfileFaces as PathProfileFaces
from PySide import QtCore
__title__ = "Path Profile based on faces Operation UI"
__author__ = "sliptonic (Brad Collette)"
__url__ = "http://www.freecadweb.org"
__doc__ = "Profile based on faces operation page controller and command implementation."
class TaskPanelOpPage(PathProfileBaseGui.TaskPanelOpPage):
'''Page controller for profile based on faces operation.'''
def profileFeatures(self):
'''profileFeatures() ... return FeatureSide | FeatureProcessing.
See PathProfileBaseGui.py for details.'''
return PathProfileBaseGui.FeatureSide | PathProfileBaseGui.FeatureProcessing
PathOpGui.SetupOperation('Profile Faces',
Command = PathOpGui.SetupOperation('Profile Faces',
PathProfileFaces.Create,
TaskPanelOpPage,
'Path-Profile-Face',