diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt
index 3735fc0419..ddfcaa519b 100644
--- a/src/Mod/Path/CMakeLists.txt
+++ b/src/Mod/Path/CMakeLists.txt
@@ -33,7 +33,6 @@ SET(PathScripts_SRCS
PathScripts/PathCircularHoleBaseGui.py
PathScripts/PathComment.py
PathScripts/PathCopy.py
- PathScripts/PathCamoticsGui.py
PathScripts/PathCustom.py
PathScripts/PathCustomGui.py
PathScripts/PathDeburr.py
diff --git a/src/Mod/Path/Gui/Resources/Path.qrc b/src/Mod/Path/Gui/Resources/Path.qrc
index cc3d6340fb..309906a1b1 100644
--- a/src/Mod/Path/Gui/Resources/Path.qrc
+++ b/src/Mod/Path/Gui/Resources/Path.qrc
@@ -14,7 +14,6 @@
icons/Path_BStep.svg
icons/Path_BStop.svg
icons/Path_BaseGeometry.svg
- icons/Path_Camotics.svg
icons/Path_Comment.svg
icons/Path_Compound.svg
icons/Path_Contour.svg
@@ -131,7 +130,6 @@
panels/ToolBitSelector.ui
panels/ToolEditor.ui
panels/ToolLibraryEditor.ui
- panels/TaskPathCamoticsSim.ui
panels/TaskPathSimulator.ui
panels/ZCorrectEdit.ui
preferences/Advanced.ui
diff --git a/src/Mod/Path/Gui/Resources/icons/Path_Camotics.svg b/src/Mod/Path/Gui/Resources/icons/Path_Camotics.svg
deleted file mode 100644
index e9d4b73037..0000000000
--- a/src/Mod/Path/Gui/Resources/icons/Path_Camotics.svg
+++ /dev/null
@@ -1,644 +0,0 @@
-
-
diff --git a/src/Mod/Path/Gui/Resources/icons/camotics-logo.png b/src/Mod/Path/Gui/Resources/icons/camotics-logo.png
deleted file mode 100644
index 652cd11b14..0000000000
Binary files a/src/Mod/Path/Gui/Resources/icons/camotics-logo.png and /dev/null differ
diff --git a/src/Mod/Path/Gui/Resources/panels/TaskPathCamoticsSim.ui b/src/Mod/Path/Gui/Resources/panels/TaskPathCamoticsSim.ui
deleted file mode 100644
index e73a4f179e..0000000000
--- a/src/Mod/Path/Gui/Resources/panels/TaskPathCamoticsSim.ui
+++ /dev/null
@@ -1,103 +0,0 @@
-
-
- TaskPathSimulator
-
-
-
- 0
- 0
- 404
- 364
-
-
-
- Path Simulator
-
-
- -
-
-
- QAbstractItemView::ContiguousSelection
-
-
- QAbstractItemView::SelectRows
-
-
-
- -
-
-
-
-
-
- Estimated time to run to the selected point in the job
-
-
- Estimated run time:
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- 40
- 20
-
-
-
-
- -
-
-
- true
-
-
-
-
-
- -
-
-
-
-
-
- 23
-
-
-
- -
-
-
- Stop
-
-
-
-
-
- -
-
-
- Launch Camotics
-
-
-
- :/icons/Path_Camotics.svg:/icons/Path_Camotics.svg
-
-
-
-
-
-
-
-
-
-
- SimStop()
- SimPlay()
- SimPause()
- SimStep()
- SimFF()
-
-
diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py
index d02be4923e..d4011741ab 100644
--- a/src/Mod/Path/InitGui.py
+++ b/src/Mod/Path/InitGui.py
@@ -78,7 +78,6 @@ class PathWorkbench (Workbench):
from PathScripts import PathToolBitLibraryCmd
import PathCommands
-
PathGuiInit.Startup()
# build commands list
@@ -97,6 +96,8 @@ class PathWorkbench (Workbench):
"Path_DressupLeadInOut", "Path_DressupRampEntry",
"Path_DressupTag", "Path_DressupZCorrect"]
extracmdlist = []
+ # modcmdmore = ["Path_Hop",]
+ # remotecmdlist = ["Path_Remote"]
specialcmdlist = []
@@ -121,12 +122,6 @@ class PathWorkbench (Workbench):
twodopcmdlist.append("Path_Slot")
if PathPreferences.advancedOCLFeaturesEnabled():
- try:
- import camotics
- toolcmdlist.append("Path_Camotics")
- except ImportError:
- pass
-
try:
import ocl # pylint: disable=unused-variable
from PathScripts import PathSurfaceGui
diff --git a/src/Mod/Path/PathScripts/PathCamoticsGui.py b/src/Mod/Path/PathScripts/PathCamoticsGui.py
deleted file mode 100644
index 125ba47bfb..0000000000
--- a/src/Mod/Path/PathScripts/PathCamoticsGui.py
+++ /dev/null
@@ -1,400 +0,0 @@
-# -*- coding: utf-8 -*-
-# ***************************************************************************
-# * Copyright (c) 2020 sliptonic *
-# * *
-# * 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.PathLog as PathLog
-# from pivy import coin
-# from itertools import cycle
-import json
-import Mesh
-import time
-import camotics
-import PathScripts.PathPost as PathPost
-import io
-import PathScripts
-import queue
-from threading import Thread, Lock
-import subprocess
-import PySide
-
-from PySide import QtCore, QtGui
-
-__title__ = "Camotics Simulator"
-__author__ = "sliptonic (Brad Collette)"
-__url__ = "https://www.freecadweb.org"
-__doc__ = "Task panel for Camotics Simulation"
-
-PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
-PathLog.trackModule(PathLog.thisModule())
-
-# Qt translation handling
-def translate(context, text, disambig=None):
- return QtCore.QCoreApplication.translate(context, text, disambig)
-
-
-class CAMoticsUI:
- def __init__(self, simulation):
- # this will create a Qt widget from our ui file
- self.form = FreeCADGui.PySideUic.loadUi(":/panels/TaskPathCamoticsSim.ui")
- self.simulation = simulation
- self.opModel = PySide.QtGui.QStandardItemModel(0, 0)#len(self.columnNames()))
- self.initializeUI()
- self.lock = False
-
- def columnNames(self):
- return ['Operation']
-
- def loadData(self):
- self.opModel.clear()
- self.opModel.setHorizontalHeaderLabels(self.columnNames())
- ops = self.simulation.finalpostlist
- for i, op in enumerate(ops):
- libItem = PySide.QtGui.QStandardItem(op.Label)
- libItem.setToolTip('op')
- libItem.setData(op)
- #libItem.setIcon(PySide.QtGui.QPixmap(':/icons/Path_ToolTable.svg'))
- self.opModel.appendRow(libItem)
-
-
- def initializeUI(self):
- self.form.progressBar.reset()
- self.updateEstimate("00:00:00")
- self.form.btnRun.setText("Run")
- self.form.btnLaunchCamotics.clicked.connect(self.launchCamotics)
- self.simulation.progressUpdate.connect(self.calculating)
- self.simulation.estimateChanged.connect(self.updateEstimate)
- self.form.btnRun.clicked.connect(self.runButton)
- self.form.lstPathObjects.setModel(self.opModel)
- self.form.lstPathObjects.selectionModel().selectionChanged.connect(self.reSelect)
- self.loadData()
- self.selectAll()
-
- def selectAll(self):
- selmodel = self.form.lstPathObjects.selectionModel()
- index0 = self.opModel.index(0, 0)
- index1 = self.opModel.index(self.opModel.rowCount()-1,0)
- itemSelection = QtCore.QItemSelection(index0, index1)
- selmodel.blockSignals(True)
- selmodel.select(itemSelection, QtCore.QItemSelectionModel.Rows | QtCore.QItemSelectionModel.Select)
- selmodel.blockSignals(False)
-
-
- def reSelect(self):
- selmodel = self.form.lstPathObjects.selectionModel()
- item = selmodel.selection().indexes()[0]
-
- index0 = self.opModel.index(0, 0)
- itemSelection = QtCore.QItemSelection(index0, item)
-
- selmodel.blockSignals(True)
- selmodel.select(itemSelection,
- QtCore.QItemSelectionModel.Rows | QtCore.QItemSelectionModel.Select)
- selmodel.blockSignals(False)
-
- selectedObjs = [self.opModel.itemFromIndex(i).data() for i in selmodel.selection().indexes()]
-
- self.simulation.setSimulationPath(selectedObjs)
-
- def runButton(self):
- if self.form.btnRun.text() == 'Run':
- self.simulation.run()
- else:
- self.simulation.stopSim()
-
-
- def updateEstimate(self, timestring):
- self.form.txtRunEstimate.setText(timestring)
-
- def launchCamotics(self):
- projfile = self.simulation.buildproject()
- subprocess.Popen(["camotics", projfile])
-
- def accept(self):
- self.simulation.accept()
- FreeCADGui.Control.closeDialog()
-
- def reject(self):
- self.simulation.cancel()
- FreeCADGui.Control.closeDialog()
-
- def calculating(self, progress=0.0):
- if progress < 1.0:
- self.form.btnRun.setText('Stop')
- else:
- self.form.btnRun.setText('Run')
- self.form.progressBar.setValue(int(progress*100))
-
-
-class CamoticsSimulation(QtCore.QObject):
-
- SIM = camotics.Simulation()
- q = queue.Queue()
- progressUpdate = QtCore.Signal(object)
- estimateChanged = QtCore.Signal(str)
-
- SHAPEMAP = {'ballend': 'Ballnose',
- 'endmill': 'Cylindrical',
- 'v-bit' : 'Conical',
- 'chamfer': 'Snubnose'}
-
- cutMaterial = None
-
- def worker(self, lock):
- while True:
- item = self.q.get()
- with lock:
- if item['TYPE'] == 'STATUS':
- if item['VALUE'] == 'DONE':
- self.SIM.wait()
- surface = self.SIM.get_surface('binary')
- #surface = self.SIM.get_surface('python')
- self.SIM.wait()
- self.addMesh(surface)
- #self.makeCoinMesh(surface)
- elif item['TYPE'] == 'PROGRESS':
- #self.taskForm.calculating(item['VALUE'])
- msg = item['VALUE']
- self.progressUpdate.emit(msg)
- self.q.task_done()
-
-
-
- def __init__(self):
- super().__init__() # needed for QT signals
- lock = Lock()
- Thread(target=self.worker, daemon=True, args=(lock,)).start()
-
- def callback(self, status, progress):
- self.q.put({'TYPE': 'PROGRESS', 'VALUE': progress})
- self.q.put({'TYPE': 'STATUS' , 'VALUE': status })
-
- def isDone(self, success):
- self.q.put({'TYPE': 'STATUS' , 'VALUE': 'DONE'})
-
- def stopSim(self):
- if self.SIM.is_running():
- self.SIM.interrupt()
- self.SIM.wait()
- return True
-
-
- def addMesh(self, surface):
- '''takes a binary stl and adds a Mesh to the current docuemnt'''
-
- if self.cutMaterial is None:
- self.cutMaterial = FreeCAD.ActiveDocument.addObject("Mesh::Feature", "SimulationOutput")
- buffer=io.BytesIO()
- buffer.write(surface)
- buffer.seek(0)
- mesh=Mesh.Mesh()
- mesh.read(buffer, "STL")
- self.cutMaterial.Mesh = mesh
- # Mesh.show(mesh)
-
- def setSimulationPath(self, postlist):
- gcode, fname = PathPost.CommandPathPost().getGcodeSilently(postlist, self.job)
- self.SIM.compute_path(gcode)
- self.SIM.wait()
-
- tot = sum([step['time'] for step in self.SIM.get_path()])
- self.estimateChanged.emit(time.strftime("%H:%M:%S", time.gmtime(tot)))
-
- def Activate(self):
- self.job = FreeCADGui.Selection.getSelectionEx()[0].Object
- postlist = PathPost.buildPostList(self.job)
- self.finalpostlist = [item for slist in postlist for item in slist]
-
- self.taskForm = CAMoticsUI(self)
- FreeCADGui.Control.showDialog(self.taskForm)
-
- self.SIM.set_metric()
- self.SIM.set_resolution('high')
-
- bb = self.job.Stock.Shape.BoundBox
- self.SIM.set_workpiece(min = (bb.XMin, bb.YMin, bb.ZMin), max = (bb.XMax, bb.YMax, bb.ZMax))
-
- for t in self.job.Tools.Group:
- self.SIM.set_tool(t.ToolNumber,
- metric = True,
- shape = self.SHAPEMAP.get(t.Tool.ShapeName, 'Cylindrical'),
- length = t.Tool.Length.Value,
- diameter = t.Tool.Diameter.Value)
-
-
- self.setSimulationPath(self.finalpostlist)
-
- def run(self):
- self.SIM.start(self.callback, done=self.isDone)
-
- #def makeCoinMesh(self, surface):
- # # this doesn't work yet
- # #sg = FreeCADGui.ActiveDocument.ActiveView.getSceneGraph();
- # color = coin.SoBaseColor()
- # color.rgb = (1, 0, 1)
- # coords = coin.SoTransform()
- # node = coin.SoSeparator()
- # node.addChild(color)
- # node.addChild(coords)
-
- # end = [-1]
- # vertices = list(zip(*[iter(surface['vertices'])] * 3))
- # #polygons = list(zip(*[iter(vertices)] * 3, cycle(end)))
- # polygons = list(zip(*[iter(range(len(vertices)))] * 3, cycle(end)))
-
- # print('verts {}'.format(len(vertices)))
- # print('polygons {}'.format(len(polygons)))
-
- # data=coin.SoCoordinate3()
- # face=coin.SoIndexedFaceSet()
- # node.addChild(data)
- # node.addChild(face)
-
- # # i = 0
- # # for i, v in enumerate(vertices):
- # # print('i: {} v: {}'.format(i, v))
- # #data.point.set1Value(i, v[0], v[1], v[2])
- # # i += 1
- # # i = 0
- # # for p in polygons:
- # # try:
- # # face.coordIndex.set1Value(i, p)
- # # i += 1
- # # except Exception as e:
- # # print(e)
- # # print(i)
- # # print(p)
-
- # # sg.addChild(node)
-
-
- def RemoveMaterial(self):
- if self.cutMaterial is not None:
- FreeCAD.ActiveDocument.removeObject(self.cutMaterial.Name)
- self.cutMaterial = None
-
- def accept(self):
- pass
-
- def cancel(self):
- self.RemoveMaterial()
-
- def buildproject(self):
-
- job = self.job
- gcode, fname = PathPost.CommandPathPost().getGcodeSilently(self.finalpostlist, self.job, temp=False)
-
- tooltemplate = {
- "units": "metric",
- "shape": "cylindrical",
- "length": 10,
- "diameter": 3.125,
- "description": ""
- }
-
- workpiecetemplate = {
- "automatic": False,
- "margin": 0,
- "bounds": {
- "min": [0, 0, 0],
- "max": [0, 0, 0]}
- }
-
- camoticstemplate = {
- "units": "metric",
- "resolution-mode": "high",
- "resolution": 1,
- "tools": {},
- "workpiece": {},
- "files": []
- }
-
- unitstring = "imperial" if FreeCAD.Units.getSchema() in [2,3,5,7] else "metric"
-
- camoticstemplate["units"] = unitstring
- camoticstemplate["resolution-mode"] = "medium"
- camoticstemplate["resolution"] = 1
-
- toollist = {}
- for t in job.Tools.Group:
- tooltemplate["units"] = unitstring
- if hasattr(t.Tool, 'Camotics'):
- tooltemplate["shape"] = t.Tool.Camotics
- else:
- tooltemplate["shape"] = self.SHAPEMAP.get(t.Tool.ShapeName, 'Cylindrical')
-
- tooltemplate["length"] = t.Tool.Length.Value
- tooltemplate["diameter"] = t.Tool.Diameter.Value
- tooltemplate["description"] = t.Label
- toollist[t.ToolNumber] = tooltemplate
-
- camoticstemplate['tools'] = toollist
-
- bb = job.Stock.Shape.BoundBox
-
- workpiecetemplate['bounds']['min'] = [bb.XMin, bb.YMin, bb.ZMin]
- workpiecetemplate['bounds']['max'] = [bb.XMax, bb.YMax, bb.ZMax]
- camoticstemplate['workpiece'] = workpiecetemplate
-
- camoticstemplate['files'] = [fname]
-
- foo = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(), "Camotics Project File")
- if foo:
- tfile = foo[0]
- else:
- return None
-
- with open(tfile, 'w') as t:
- proj=json.dumps(camoticstemplate, indent=2)
- t.write(proj)
-
- return tfile #json.dumps(camoticstemplate, indent=2)
-
-
-class CommandCamoticsSimulate:
- def GetResources(self):
- return {'Pixmap': 'Path_Camotics',
- 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Camotics", "Camotics"),
- 'Accel': "P, C",
- 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Camotics", "Simulate using Camotics"),
- 'CmdType': "ForEdit"}
-
- def IsActive(self):
- if bool(FreeCADGui.Selection.getSelection()) is False:
- return False
- try:
- job = FreeCADGui.Selection.getSelectionEx()[0].Object
- return isinstance(job.Proxy, PathScripts.PathJob.ObjectJob)
- except:
- return False
-
- def Activated(self):
- pathSimulation.Activate()
-
-
-pathSimulation = CamoticsSimulation()
-
-if FreeCAD.GuiUp:
- FreeCADGui.addCommand('Path_Camotics', CommandCamoticsSimulate())
-
-
-FreeCAD.Console.PrintLog("Loading PathCamoticsSimulateGui ... done\n")
diff --git a/src/Mod/Path/PathScripts/PathPost.py b/src/Mod/Path/PathScripts/PathPost.py
index 1a0699b801..3c8facd865 100644
--- a/src/Mod/Path/PathScripts/PathPost.py
+++ b/src/Mod/Path/PathScripts/PathPost.py
@@ -33,7 +33,6 @@ import PathScripts.PathPreferences as PathPreferences
import PathScripts.PathUtil as PathUtil
import PathScripts.PathUtils as PathUtils
import os
-import tempfile
from PathScripts.PathPostProcessor import PostProcessor
from PySide import QtCore, QtGui
@@ -57,187 +56,6 @@ class _TempObject:
Label = "Fixture"
-def resolveFileName(job):
-
- path = PathPreferences.defaultOutputFile()
- if job.PostProcessorOutputFile:
- path = job.PostProcessorOutputFile
- filename = path
-
- if '%D' in filename:
- D = FreeCAD.ActiveDocument.FileName
- if D:
- D = os.path.dirname(D)
- # in case the document is in the current working directory
- if not D:
- D = '.'
- else:
- FreeCAD.Console.PrintError("Please save document in order to resolve output path!\n")
- return None
- filename = filename.replace('%D', D)
-
- if '%d' in filename:
- d = FreeCAD.ActiveDocument.Label
- filename = filename.replace('%d', d)
-
- if '%j' in filename:
- j = job.Label
- filename = filename.replace('%j', j)
-
- if '%M' in filename:
- pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")
- M = pref.GetString("MacroPath", FreeCAD.getUserAppDataDir())
- filename = filename.replace('%M', M)
-
-
- policy = PathPreferences.defaultOutputPolicy()
-
- openDialog = policy == 'Open File Dialog'
- if os.path.isdir(filename) or not os.path.isdir(os.path.dirname(filename)):
- # Either the entire filename resolves into a directory or the parent directory doesn't exist.
- # Either way I don't know what to do - ask for help
- openDialog = True
-
- if os.path.isfile(filename) and not openDialog:
- if policy == 'Open File Dialog on conflict':
- openDialog = True
- elif policy == 'Append Unique ID on conflict':
- fn, ext = os.path.splitext(filename)
- nr = fn[-3:]
- n = 1
- if nr.isdigit():
- n = int(nr)
- while os.path.isfile("%s%03d%s" % (fn, n, ext)):
- n = n + 1
- filename = "%s%03d%s" % (fn, n, ext)
-
- if openDialog:
- foo = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(), "Output File", filename)
- if foo[0]:
- filename = foo[0]
- else:
- filename = None
-
- return filename
-
-def buildPostList(job):
- ''' Takes the job and determines the specific objects and order to
- postprocess Returns a list of objects which can be passed to
- exportObjectsWith() for final posting'''
- wcslist = job.Fixtures
- orderby = job.OrderOutputBy
-
- postlist = []
-
- if orderby == 'Fixture':
- PathLog.debug("Ordering by Fixture")
- # Order by fixture means all operations and tool changes will be completed in one
- # fixture before moving to the next.
-
- currTool = None
- for index, f in enumerate(wcslist):
- # create an object to serve as the fixture path
- fobj = _TempObject()
- c1 = Path.Command(f)
- fobj.Path = Path.Path([c1])
- if index != 0:
- c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
- fobj.Path.addCommands(c2)
- fobj.InList.append(job)
- sublist = [fobj]
-
- # Now generate the gcode
- for obj in job.Operations.Group:
- tc = PathUtil.toolControllerForOp(obj)
- if tc is not None and PathUtil.opProperty(obj, 'Active'):
- if tc.ToolNumber != currTool:
- sublist.append(tc)
- PathLog.debug("Appending TC: {}".format(tc.Name))
- currTool = tc.ToolNumber
- sublist.append(obj)
- postlist.append(sublist)
-
- elif orderby == 'Tool':
- PathLog.debug("Ordering by Tool")
- # Order by tool means tool changes are minimized.
- # all operations with the current tool are processed in the current
- # fixture before moving to the next fixture.
-
- currTool = None
- fixturelist = []
- for f in wcslist:
- # create an object to serve as the fixture path
- fobj = _TempObject()
- c1 = Path.Command(f)
- c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
- fobj.Path = Path.Path([c1, c2])
- fobj.InList.append(job)
- fixturelist.append(fobj)
-
- # Now generate the gcode
- curlist = [] # list of ops for tool, will repeat for each fixture
- sublist = [] # list of ops for output splitting
-
- for idx, obj in enumerate(job.Operations.Group):
-
- # check if the operation is active
- active = PathUtil.opProperty(obj, 'Active')
-
- tc = PathUtil.toolControllerForOp(obj)
- if tc is None or tc.ToolNumber == currTool and active:
- curlist.append(obj)
- elif tc.ToolNumber != currTool and currTool is None and active: # first TC
- sublist.append(tc)
- curlist.append(obj)
- currTool = tc.ToolNumber
- elif tc.ToolNumber != currTool and currTool is not None and active: # TC
- for fixture in fixturelist:
- sublist.append(fixture)
- sublist.extend(curlist)
- postlist.append(sublist)
- sublist = [tc]
- curlist = [obj]
- currTool = tc.ToolNumber
-
- if idx == len(job.Operations.Group) - 1: # Last operation.
- for fixture in fixturelist:
- sublist.append(fixture)
- sublist.extend(curlist)
- postlist.append(sublist)
-
- elif orderby == 'Operation':
- PathLog.debug("Ordering by Operation")
- # Order by operation means ops are done in each fixture in
- # sequence.
- currTool = None
- firstFixture = True
-
- # Now generate the gcode
- for obj in job.Operations.Group:
- if PathUtil.opProperty(obj, 'Active'):
- sublist = []
- PathLog.debug("obj: {}".format(obj.Name))
- for f in wcslist:
- fobj = _TempObject()
- c1 = Path.Command(f)
- fobj.Path = Path.Path([c1])
- if not firstFixture:
- c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
- fobj.Path.addCommands(c2)
- fobj.InList.append(job)
- sublist.append(fobj)
- firstFixture = False
- tc = PathUtil.toolControllerForOp(obj)
- if tc is not None:
- if job.SplitOutput or (tc.ToolNumber != currTool):
- sublist.append(tc)
- currTool = tc.ToolNumber
- sublist.append(obj)
- postlist.append(sublist)
-
- return postlist
-
-
class DlgSelectPostProcessor:
def __init__(self, parent=None):
@@ -279,6 +97,73 @@ class CommandPathPost:
# pylint: disable=no-init
subpart = 1
+ def resolveFileName(self, job):
+ path = PathPreferences.defaultOutputFile()
+ if job.PostProcessorOutputFile:
+ path = job.PostProcessorOutputFile
+ filename = path
+
+ if '%D' in filename:
+ D = FreeCAD.ActiveDocument.FileName
+ if D:
+ D = os.path.dirname(D)
+ # in case the document is in the current working directory
+ if not D:
+ D = '.'
+ else:
+ FreeCAD.Console.PrintError("Please save document in order to resolve output path!\n")
+ return None
+ filename = filename.replace('%D', D)
+
+ if '%d' in filename:
+ d = FreeCAD.ActiveDocument.Label
+ filename = filename.replace('%d', d)
+
+ if '%j' in filename:
+ j = job.Label
+ filename = filename.replace('%j', j)
+
+ if '%M' in filename:
+ pref = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")
+ M = pref.GetString("MacroPath", FreeCAD.getUserAppDataDir())
+ filename = filename.replace('%M', M)
+
+ if '%s' in filename:
+ if job.SplitOutput:
+ filename = filename.replace('%s', '_'+str(self.subpart))
+ self.subpart += 1
+ else:
+ filename = filename.replace('%s', '')
+
+ policy = PathPreferences.defaultOutputPolicy()
+
+ openDialog = policy == 'Open File Dialog'
+ if os.path.isdir(filename) or not os.path.isdir(os.path.dirname(filename)):
+ # Either the entire filename resolves into a directory or the parent directory doesn't exist.
+ # Either way I don't know what to do - ask for help
+ openDialog = True
+
+ if os.path.isfile(filename) and not openDialog:
+ if policy == 'Open File Dialog on conflict':
+ openDialog = True
+ elif policy == 'Append Unique ID on conflict':
+ fn, ext = os.path.splitext(filename)
+ nr = fn[-3:]
+ n = 1
+ if nr.isdigit():
+ n = int(nr)
+ while os.path.isfile("%s%03d%s" % (fn, n, ext)):
+ n = n + 1
+ filename = "%s%03d%s" % (fn, n, ext)
+
+ if openDialog:
+ foo = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(), "Output File", filename)
+ if foo[0]:
+ filename = foo[0]
+ else:
+ filename = None
+
+ return filename
def resolvePostProcessor(self, job):
if hasattr(job, "PostProcessor"):
@@ -305,37 +190,6 @@ class CommandPathPost:
return False
- def getGcodeSilently(self, objs, job, temp=True):
- '''This method will postprocess the given objects without opening dialogs
- or prompting the user for any input. gcode is returned as a list of lists
- suitable for splitting'''
- PathLog.track('objs: {}'.format(objs))
-
- postArgs = PathPreferences.defaultPostProcessorArgs()
- if hasattr(job, "PostProcessorArgs") and job.PostProcessorArgs:
- postArgs = job.PostProcessorArgs
- elif hasattr(job, "PostProcessor") and job.PostProcessor:
- postArgs = ''
-
- if not '--no-show-editor' in postArgs:
- postArgs += ' --no-show-editor'
-
- PathLog.track('postArgs: {}'.format(postArgs))
-
- if temp:
- tfile = tempfile.NamedTemporaryFile()
- filename = tfile.name
- else:
- filename = resolveFileName(job)
- tfile = open(filename, "w")
-
- with tfile:
- postname = self.resolvePostProcessor(job)
- processor = PostProcessor.load(postname)
- gcode = processor.export(objs, filename, postArgs)
- return gcode, filename
-
-
def exportObjectsWith(self, objs, job, needFilename=True):
PathLog.track()
# check if the user has a project and has set the default post and
@@ -349,7 +203,7 @@ class CommandPathPost:
postname = self.resolvePostProcessor(job)
filename = '-'
if postname and needFilename:
- filename = resolveFileName(job)
+ filename = self.resolveFileName(job)
if postname and filename:
print("post: %s(%s, %s)" % (postname, filename, postArgs))
@@ -407,9 +261,117 @@ class CommandPathPost:
PathLog.debug("about to postprocess job: {}".format(job.Name))
- postlist = buildPostList(job)
- filename = resolveFileName(job)
+ wcslist = job.Fixtures
+ orderby = job.OrderOutputBy
+ split = job.SplitOutput
+ postlist = []
+
+ if orderby == 'Fixture':
+ PathLog.debug("Ordering by Fixture")
+ # Order by fixture means all operations and tool changes will be completed in one
+ # fixture before moving to the next.
+
+ currTool = None
+ for index, f in enumerate(wcslist):
+ # create an object to serve as the fixture path
+ fobj = _TempObject()
+ c1 = Path.Command(f)
+ fobj.Path = Path.Path([c1])
+ if index != 0:
+ c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
+ fobj.Path.addCommands(c2)
+ fobj.InList.append(job)
+ sublist = [fobj]
+
+ # Now generate the gcode
+ for obj in job.Operations.Group:
+ tc = PathUtil.toolControllerForOp(obj)
+ if tc is not None and PathUtil.opProperty(obj, 'Active'):
+ if tc.ToolNumber != currTool:
+ sublist.append(tc)
+ PathLog.debug("Appending TC: {}".format(tc.Name))
+ currTool = tc.ToolNumber
+ sublist.append(obj)
+ postlist.append(sublist)
+
+ elif orderby == 'Tool':
+ PathLog.debug("Ordering by Tool")
+ # Order by tool means tool changes are minimized.
+ # all operations with the current tool are processed in the current
+ # fixture before moving to the next fixture.
+
+ currTool = None
+ fixturelist = []
+ for f in wcslist:
+ # create an object to serve as the fixture path
+ fobj = _TempObject()
+ c1 = Path.Command(f)
+ c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
+ fobj.Path = Path.Path([c1, c2])
+ fobj.InList.append(job)
+ fixturelist.append(fobj)
+
+ # Now generate the gcode
+ curlist = [] # list of ops for tool, will repeat for each fixture
+ sublist = [] # list of ops for output splitting
+
+ for idx, obj in enumerate(job.Operations.Group):
+
+ # check if the operation is active
+ active = PathUtil.opProperty(obj, 'Active')
+
+ tc = PathUtil.toolControllerForOp(obj)
+ if tc is None or tc.ToolNumber == currTool and active:
+ curlist.append(obj)
+ elif tc.ToolNumber != currTool and currTool is None and active: # first TC
+ sublist.append(tc)
+ curlist.append(obj)
+ currTool = tc.ToolNumber
+ elif tc.ToolNumber != currTool and currTool is not None and active: # TC
+ for fixture in fixturelist:
+ sublist.append(fixture)
+ sublist.extend(curlist)
+ postlist.append(sublist)
+ sublist = [tc]
+ curlist = [obj]
+ currTool = tc.ToolNumber
+
+ if idx == len(job.Operations.Group) - 1: # Last operation.
+ for fixture in fixturelist:
+ sublist.append(fixture)
+ sublist.extend(curlist)
+ postlist.append(sublist)
+
+ elif orderby == 'Operation':
+ PathLog.debug("Ordering by Operation")
+ # Order by operation means ops are done in each fixture in
+ # sequence.
+ currTool = None
+ firstFixture = True
+
+ # Now generate the gcode
+ for obj in job.Operations.Group:
+ if PathUtil.opProperty(obj, 'Active'):
+ sublist = []
+ PathLog.debug("obj: {}".format(obj.Name))
+ for f in wcslist:
+ fobj = _TempObject()
+ c1 = Path.Command(f)
+ fobj.Path = Path.Path([c1])
+ if not firstFixture:
+ c2 = Path.Command("G0 Z" + str(job.Stock.Shape.BoundBox.ZMax + job.SetupSheet.ClearanceHeightOffset.Value))
+ fobj.Path.addCommands(c2)
+ fobj.InList.append(job)
+ sublist.append(fobj)
+ firstFixture = False
+ tc = PathUtil.toolControllerForOp(obj)
+ if tc is not None:
+ if job.SplitOutput or (tc.ToolNumber != currTool):
+ sublist.append(tc)
+ currTool = tc.ToolNumber
+ sublist.append(obj)
+ postlist.append(sublist)
fail = True
rc = ''