From a440dabf5d968ff5ecea29b8bbc63d40a5964d62 Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Sat, 26 Aug 2017 14:08:16 -0700 Subject: [PATCH] Add tool controller support with inplace editing. --- src/Mod/Path/Gui/Resources/panels/PathEdit.ui | 16 +-- src/Mod/Path/PathScripts/PathJob.py | 15 +- src/Mod/Path/PathScripts/PathJobGui.py | 135 +++++++++++++++++- .../Path/PathScripts/PathToolController.py | 69 +++++---- .../PathScripts/PathToolLibraryManager.py | 33 +++-- 5 files changed, 204 insertions(+), 64 deletions(-) diff --git a/src/Mod/Path/Gui/Resources/panels/PathEdit.ui b/src/Mod/Path/Gui/Resources/panels/PathEdit.ui index f1897a0bcb..9cd9925915 100644 --- a/src/Mod/Path/Gui/Resources/panels/PathEdit.ui +++ b/src/Mod/Path/Gui/Resources/panels/PathEdit.ui @@ -14,7 +14,7 @@ Job Edit - 3 + 2 @@ -516,9 +516,9 @@ - - - 71 + + + false @@ -527,7 +527,7 @@ - Tool + Nr. @@ -551,7 +551,7 @@ - + false @@ -561,14 +561,14 @@ - + Add - + false diff --git a/src/Mod/Path/PathScripts/PathJob.py b/src/Mod/Path/PathScripts/PathJob.py index eb7d11a805..eeb9ef7a2a 100644 --- a/src/Mod/Path/PathScripts/PathJob.py +++ b/src/Mod/Path/PathScripts/PathJob.py @@ -35,7 +35,7 @@ from PySide import QtCore if True: PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule()) - PathLog.trackModule() + PathLog.trackModule(PathLog.thisModule()) else: PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule()) @@ -94,6 +94,7 @@ class ObjectJob: self.assignTemplate(obj, template) def onDelete(self, obj, arg2=None): + PathLog.track(obj, arg2) for tc in obj.ToolController: FreeCAD.ActiveDocument.removeObject(tc.Name) obj.ToolController = [] @@ -129,9 +130,9 @@ class ObjectJob: if job.get(JobTemplate.Description): obj.Description = job.get(JobTemplate.Description) for tc in tree.getroot().iter(JobTemplate.ToolController): - tcs.append(PathToolController.CommandPathToolController.FromTemplate(tc)) + tcs.append(PathToolController.FromTemplate(tc)) else: - tcs.append(PathToolController.CommandPathToolController.Create(obj.Name)) + tcs.append(PathToolController.Create(obj.Name)) PathLog.debug("setting tool controllers (%d)" % len(tcs)) obj.ToolController = tcs @@ -167,6 +168,14 @@ class ObjectJob: group.append(op) self.obj.Operations.Group = group + def addToolController(self, tc): + group = self.obj.ToolController + PathLog.info("addToolController(%s): %s" % (tc.Label, [t.Label for t in group])) + if tc.Name not in [str(t.Name) for t in group]: + group.append(tc) + self.obj.ToolController = group + + @classmethod def baseCandidates(cls): '''Answer all objects in the current document which could serve as a Base for a job.''' diff --git a/src/Mod/Path/PathScripts/PathJobGui.py b/src/Mod/Path/PathScripts/PathJobGui.py index 56227d0206..c768f08502 100644 --- a/src/Mod/Path/PathScripts/PathJobGui.py +++ b/src/Mod/Path/PathScripts/PathJobGui.py @@ -27,6 +27,7 @@ import FreeCADGui import PathScripts.PathJob as PathJob import PathScripts.PathLog as PathLog import PathScripts.PathToolController as PathToolController +import PathScripts.PathToolLibraryManager as PathToolLibraryManager import sys from PathScripts.PathPreferences import PathPreferences @@ -86,6 +87,7 @@ class ViewProvider: class TaskPanel: DataObject = QtCore.Qt.ItemDataRole.UserRole + DataProperty = QtCore.Qt.ItemDataRole.UserRole + 1 def __init__(self, vobj, deleteOnReject): FreeCAD.ActiveDocument.openTransaction(translate("Path_Job", "Edit Job")) @@ -94,6 +96,9 @@ class TaskPanel: self.deleteOnReject = deleteOnReject self.form = FreeCADGui.PySideUic.loadUi(":/panels/PathEdit.ui") + self.form.toolControllerList.horizontalHeader().setResizeMode(0, QtGui.QHeaderView.Stretch) + self.form.toolControllerList.resizeColumnsToContents() + currentPostProcessor = self.obj.PostProcessor postProcessors = PathPreferences.allEnabledPostProcessors(['', currentPostProcessor]) for post in postProcessors: @@ -168,6 +173,58 @@ class TaskPanel: widget.setCurrentIndex(index) widget.blockSignals(False) + def updateToolController(self): + self.form.toolControllerList.blockSignals(True) + self.form.toolControllerList.clearContents() + self.form.toolControllerList.setRowCount(0) + + self.form.activeToolController.blockSignals(True) + index = self.form.activeToolController.currentIndex() + select = None if index == -1 else self.form.activeToolController.itemData(index) + self.form.activeToolController.clear() + + for row,tc in enumerate(sorted(self.obj.ToolController, key=lambda tc: tc.Label)): + self.form.activeToolController.addItem(tc.Label, tc) + if tc == select: + index = row + + self.form.toolControllerList.insertRow(row) + + item = QtGui.QTableWidgetItem(tc.Label) + item.setData(self.DataObject, tc) + item.setData(self.DataProperty, 'Label') + self.form.toolControllerList.setItem(row, 0, item) + + item = QtGui.QTableWidgetItem("%d" % tc.ToolNumber) + item.setTextAlignment(QtCore.Qt.AlignRight) + item.setData(self.DataObject, tc) + item.setData(self.DataProperty, 'Number') + self.form.toolControllerList.setItem(row, 1, item) + + item = QtGui.QTableWidgetItem("%g" % tc.HorizFeed) + item.setTextAlignment(QtCore.Qt.AlignRight) + item.setData(self.DataObject, tc) + item.setData(self.DataProperty, 'HorizFeed') + self.form.toolControllerList.setItem(row, 2, item) + + item = QtGui.QTableWidgetItem("%g" % tc.VertFeed) + item.setTextAlignment(QtCore.Qt.AlignRight) + item.setData(self.DataObject, tc) + item.setData(self.DataProperty, 'VertFeed') + self.form.toolControllerList.setItem(row, 3, item) + + item = QtGui.QTableWidgetItem("%s%g" % ('+' if tc.SpindleDir == 'Forward' else '-', tc.SpindleSpeed)) + item.setTextAlignment(QtCore.Qt.AlignRight) + item.setData(self.DataObject, tc) + item.setData(self.DataProperty, 'Spindle') + self.form.toolControllerList.setItem(row, 4, item) + + if index != -1: + self.form.activeToolController.setCurrentIndex(index) + + self.form.activeToolController.blockSignals(False) + self.form.toolControllerList.blockSignals(False) + def setFields(self): '''sets fields in the form to match the object''' @@ -194,6 +251,7 @@ class TaskPanel: if baseindex >= 0: self.form.infoModel.setCurrentIndex(baseindex) + self.updateToolController() def setPostProcessorOutputFile(self): filename = QtGui.QFileDialog.getSaveFileName(self.form, translate("Path_Job", "Select Output File"), None, translate("Path_Job", "All Files (*.*)")) @@ -207,14 +265,80 @@ class TaskPanel: else: self.form.operationModify.setEnabled(False) - def operationDelete(self): - for item in self.form.operationsList.selectedItems(): + def objectDelete(self, widget): + for item in widget.selectedItems(): obj = item.data(self.DataObject) if obj.ViewObject and hasattr(obj.ViewObject, 'Proxy') and hasattr(obj.ViewObject.Proxy, 'onDelete'): obj.ViewObject.Proxy.onDelete(obj.ViewObject, None) FreeCAD.ActiveDocument.removeObject(obj.Name) self.setFields() + def operationDelete(self): + self.objectDelete(self.form.operationsList) + + def toolControllerSelect(self): + def canDeleteTC(tc): + # if the TC is referenced anywhere but the job we don't want to delete it + return len(tc.InList) == 1 + + # if anything is selected it can be edited + edit = True if self.form.toolControllerList.selectedItems() else False + self.form.toolControllerEdit.setEnabled(edit) + + # can only delete what is selected + delete = edit + # ... but we want to make sure there's at least one TC left + if len(self.obj.ToolController) == len(self.form.toolControllerList.selectedItems()): + delete = False + # ... also don't want to delete any TCs that are already used + if delete: + for item in self.form.toolControllerList.selectedItems(): + if not canDeleteTC(item.data(self.DataObject)): + delete = False + break + self.form.toolControllerDelete.setEnabled(delete) + + def toolControllerEdit(self): + pass + + def toolControllerAdd(self): + PathToolLibraryManager.CommandToolLibraryEdit().edit(self.obj, self.updateToolController) + + def toolControllerDelete(self): + self.objectDelete(self.form.toolControllerList) + + def toolControllerChanged(self, item): + tc = item.data(self.DataObject) + prop = item.data(self.DataProperty) + if 'Label' == prop: + tc.Label = item.text() + item.setText(tc.Label) + elif 'Number' == prop: + try: + tc.ToolNumber = int(item.text()) + except: + pass + item.setText("%d" % tc.ToolNumber) + elif 'Spindle' == prop: + try: + speed = float(item.text()) + rot = 'Forward' + if speed < 0: + rot = 'Reverse' + speed = -speed + tc.SpindleDir = rot + tc.SpindleSpeed = speed + except: + pass + item.setText("%s%g" % ('+' if tc.SpindleDir == 'Forward' else '-', tc.SpindleSpeed)) + else: + try: + val = FreeCAD.Units.Quantity(item.text()) + setattr(tc, prop, val) + except: + pass + item.setText("%g" % getattr(tc, prop).Value) + def setupUi(self): self.setFields() @@ -232,7 +356,14 @@ class TaskPanel: self.form.operationsList.indexesMoved.connect(self.getFields) self.form.operationDelete.clicked.connect(self.operationDelete) + self.form.toolControllerList.itemSelectionChanged.connect(self.toolControllerSelect) + self.form.toolControllerList.itemChanged.connect(self.toolControllerChanged) + self.form.toolControllerEdit.clicked.connect(self.toolControllerEdit) + self.form.toolControllerDelete.clicked.connect(self.toolControllerDelete) + self.form.toolControllerAdd.clicked.connect(self.toolControllerAdd) + self.operationSelect() + self.toolControllerSelect() def Create(base, template=None): '''Create(base, template) ... creates a job instance for the given base object diff --git a/src/Mod/Path/PathScripts/PathToolController.py b/src/Mod/Path/PathScripts/PathToolController.py index 15ccf73c68..d1bb6d69c1 100644 --- a/src/Mod/Path/PathScripts/PathToolController.py +++ b/src/Mod/Path/PathScripts/PathToolController.py @@ -150,7 +150,7 @@ class ToolController: return obj.Tool -class _ViewProviderToolController: +class ViewProvider: def __init__(self, vobj): vobj.Proxy = self @@ -203,6 +203,37 @@ class _ViewProviderToolController: # this is executed when the user cancels or terminates edit mode return False +def Create(name = 'Default Tool', tool=None, toolNumber=1, assignViewProvider=True): + PathLog.track(tool, toolNumber) + + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name) + ToolController(obj) + if assignViewProvider: + ViewProvider(obj.ViewObject) + + if tool is None: + tool = Path.Tool() + tool.Diameter = 5.0 + tool.Name = "Default Tool" + tool.CuttingEdgeHeight = 15.0 + tool.ToolType = "EndMill" + tool.Material = "HighSpeedSteel" + obj.Tool = tool + obj.ToolNumber = toolNumber + return obj + +def FromTemplate(template, assignViewProvider=True): + PathLog.track() + + obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", template.get(ToolControllerTemplate.Label)) + tc = ToolController(obj) + if assignViewProvider: + ViewProvider(obj.ViewObject) + + tc.assignTemplate(obj, template) + + return obj + class CommandPathToolController: def GetResources(self): @@ -219,41 +250,7 @@ class CommandPathToolController: def Activated(self): PathLog.track() - self.Create() - - @staticmethod - def Create(assignViewProvider=True, tool=None, toolNumber=1): - PathLog.track("tool: {} with toolNumber: {}".format(tool, toolNumber)) - - obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", "Default Tool") - PathScripts.PathToolController.ToolController(obj) - if FreeCAD.GuiUp and assignViewProvider: - PathScripts.PathToolController._ViewProviderToolController(obj.ViewObject) - - if tool is None: - tool = Path.Tool() - tool.Diameter = 5.0 - tool.Name = "Default Tool" - tool.CuttingEdgeHeight = 15.0 - tool.ToolType = "EndMill" - tool.Material = "HighSpeedSteel" - obj.Tool = tool - obj.ToolNumber = toolNumber - return obj - - @staticmethod - def FromTemplate(template, assignViewProvider=True): - PathLog.track() - - obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", template.get(ToolControllerTemplate.Label)) - tc = PathScripts.PathToolController.ToolController(obj) - if assignViewProvider: - PathScripts.PathToolController._ViewProviderToolController(obj.ViewObject) - - tc.assignTemplate(obj, template) - - return obj - + Create() class TaskPanel: def __init__(self): diff --git a/src/Mod/Path/PathScripts/PathToolLibraryManager.py b/src/Mod/Path/PathScripts/PathToolLibraryManager.py index b40864918d..5959f04d94 100644 --- a/src/Mod/Path/PathScripts/PathToolLibraryManager.py +++ b/src/Mod/Path/PathScripts/PathToolLibraryManager.py @@ -335,7 +335,8 @@ class ToolLibraryManager(): class EditorPanel(): - def __init__(self): + + def __init__(self, job, cb): #self.form = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/ToolLibraryEditor.ui") self.form = FreeCADGui.PySideUic.loadUi(":/panels/ToolLibraryEditor.ui") #self.editform = FreeCADGui.PySideUic.loadUi(FreeCAD.getHomePath() + "Mod/Path/ToolEdit.ui") @@ -344,6 +345,8 @@ class EditorPanel(): self.loadTable() self.form.ToolsList.resizeColumnsToContents() + self.job = job + self.cb = cb def accept(self): pass @@ -545,18 +548,18 @@ class EditorPanel(): for toolnum in tools: tool = self.TLM.getTool(currList, int(toolnum)) PathLog.debug('tool: {}, toolnum: {}'.format(tool, toolnum)) - for job in FreeCAD.ActiveDocument.findObjects("Path::Feature"): - if isinstance(job.Proxy, PathScripts.PathJob.ObjectJob) and job.Label == targetlist: - - label = "T{}: {}".format(toolnum, tool.Name) - obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython",label) - PathScripts.PathToolController.ToolController(obj) - PathScripts.PathToolController._ViewProviderToolController(obj.ViewObject) - PathUtils.addToJob(obj, job.Name) - FreeCAD.activeDocument().recompute() - obj.Tool = tool.copy() - obj.ToolNumber = int(toolnum) - #obj.recompute() + if self.job: + label = "T{}: {}".format(toolnum, tool.Name) + tc = PathScripts.PathToolController.Create(label, tool=tool, toolNumber=int(toolnum)) + self.job.Proxy.addToolController(tc) + else: + for job in FreeCAD.ActiveDocument.findObjects("Path::Feature"): + if isinstance(job.Proxy, PathScripts.PathJob.ObjectJob) and job.Label == targetlist: + label = "T{}: {}".format(toolnum, tool.Name) + tc = PathScripts.PathToolController.Create(label, tool=tool, toolNumber=int(toolnum)) + job.Proxy.addToolController(tc) + if self.cb: + self.cb() FreeCAD.ActiveDocument.recompute() def getStandardButtons(self): @@ -579,8 +582,8 @@ class EditorPanel(): self.setFields() class CommandToolLibraryEdit(): - def edit(self): - editor = EditorPanel() + def edit(self, job=None, cb=None): + editor = EditorPanel(job, cb) editor.setupUi() r = editor.form.exec_()