diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py index 4cc2c8f4f8a4..4cc94a460368 100644 --- a/src/Mod/Path/InitGui.py +++ b/src/Mod/Path/InitGui.py @@ -1,31 +1,32 @@ -#*************************************************************************** -#* (c) Yorik van Havre (yorik@uncreated.net) 2014 * -#* * -#* This file is part of the FreeCAD CAx development system. * -#* * -#* 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. * -#* * -#* FreeCAD 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 Lesser General Public License for more details. * -#* * -#* You should have received a copy of the GNU Library General Public * -#* License along with FreeCAD; if not, write to the Free Software * -#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * -#* USA * -#* * -#***************************************************************************/ +# *************************************************************************** +# * (c) Yorik van Havre (yorik@uncreated.net) 2014 * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * 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. * +# * * +# * FreeCAD 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 Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with FreeCAD; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# ***************************************************************************/ - -class PathWorkbench ( Workbench ): +class PathWorkbench (Workbench): "Path workbench" + def __init__(self): - self.__class__.Icon = FreeCAD.getResourceDir() + "Mod/Path/Resources/icons/PathWorkbench.svg" + self.__class__.Icon = FreeCAD.getResourceDir( + ) + "Mod/Path/Resources/icons/PathWorkbench.svg" self.__class__.MenuText = "Path" self.__class__.ToolTip = "Path workbench" @@ -65,59 +66,73 @@ def Initialize(self): from PathScripts import PathEngrave from PathScripts import PathSurface from PathScripts import PathRemote + from PathScripts import PathSanity # build commands list - projcmdlist = ["Path_Project", "Path_ToolTableEdit","Path_Post","Path_Inspect"] - prepcmdlist = ["Path_Plane","Path_Fixture","Path_LoadTool","Path_ToolLenOffset","Path_Comment","Path_Stop","Path_FaceProfile","Path_FacePocket","Path_Custom","Path_FromShape"] - opcmdlist = ["Path_Profile","Path_Pocket","Path_Drilling","Path_Engrave","Path_Surfacing"] - modcmdlist = ["Path_Copy","Path_CompoundExtended","Path_Dressup","Path_Hop","Path_Array","Path_SimpleCopy"] + projcmdlist = ["Path_Project", "Path_ToolTableEdit", + "Path_Post", "Path_Inspect", "Path_Sanity"] + prepcmdlist = ["Path_Plane", "Path_Fixture", "Path_LoadTool", "Path_ToolLenOffset", "Path_Comment", + "Path_Stop", "Path_FaceProfile", "Path_FacePocket", "Path_Custom", "Path_FromShape"] + opcmdlist = ["Path_Profile", "Path_Pocket", + "Path_Drilling", "Path_Engrave", "Path_Surfacing"] + modcmdlist = ["Path_Copy", "Path_CompoundExtended", + "Path_Dressup", "Path_Hop", "Path_Array", "Path_SimpleCopy"] remotecmdlist = ["Path_Remote"] # Add commands to menu and toolbar - def QT_TRANSLATE_NOOP(scope, text): + def QT_TRANSLATE_NOOP(scope, text): return text - def translate(context,text): + + def translate(context, text): return QtGui.QApplication.translate(context, text, None, QtGui.QApplication.UnicodeUTF8).encode("utf8") - self.appendToolbar(translate("Path","Project Setup"),projcmdlist) - self.appendToolbar(translate("Path","Partial Commands"),prepcmdlist) - self.appendToolbar(translate("Path","New Operations"),opcmdlist) - self.appendToolbar(translate("Path","Path Modification"),modcmdlist) + self.appendToolbar(translate("Path", "Project Setup"), projcmdlist) + self.appendToolbar(translate("Path", "Partial Commands"), prepcmdlist) + self.appendToolbar(translate("Path", "New Operations"), opcmdlist) + self.appendToolbar(translate("Path", "Path Modification"), modcmdlist) + + self.appendMenu([translate("Path", "Path"), translate( + "Path", "Project Setup")], projcmdlist) + self.appendMenu([translate("Path", "Path"), translate( + "Path", "Partial Commands")], prepcmdlist) + self.appendMenu([translate("Path", "Path"), translate( + "Path", "New Operations")], opcmdlist) + self.appendMenu([translate("Path", "Path"), translate( + "Path", "Path Modification")], modcmdlist) + self.appendMenu([translate("Path", "Path"), translate( + "Path", "Remote Operations")], remotecmdlist) - self.appendMenu([translate("Path","Path"),translate("Path","Project Setup")],projcmdlist) - self.appendMenu([translate("Path","Path"),translate("Path","Partial Commands")],prepcmdlist) - self.appendMenu([translate("Path","Path"),translate("Path","New Operations")],opcmdlist) - self.appendMenu([translate("Path","Path"),translate("Path","Path Modification")],modcmdlist) - self.appendMenu([translate("Path","Path"),translate("Path","Remote Operations")],remotecmdlist) - # Add preferences pages import os - FreeCADGui.addPreferencePage(FreeCAD.getHomePath()+os.sep+"Mod"+os.sep+"Path"+os.sep+"PathScripts"+os.sep+"DlgSettingsPath.ui","Path") - - Log ('Loading Path workbench... done\n') + FreeCADGui.addPreferencePage(FreeCAD.getHomePath( + ) + os.sep + "Mod" + os.sep + "Path" + os.sep + "PathScripts" + os.sep + "DlgSettingsPath.ui", "Path") + + Log('Loading Path workbench... done\n') def GetClassName(self): return "Gui::PythonWorkbench" - + def Activated(self): # update the translation engine FreeCADGui.updateLocale() Msg("Path workbench activated\n") - + def Deactivated(self): Msg("Path workbench deactivated\n") - + def ContextMenu(self, recipient): if len(FreeCADGui.Selection.getSelection()) == 1: if FreeCADGui.Selection.getSelection()[0].isDerivedFrom("Path::Feature"): - self.appendContextMenu("",["Path_Inspect"]) - if "Profile" in FreeCADGui.Selection.getSelection()[0].Name: - self.appendContextMenu("",["Add_Tag"]) - self.appendContextMenu("",["Set_StartPoint"]) - self.appendContextMenu("",["Set_EndPoint"]) - if "Remote" in FreeCADGui.Selection.getSelection()[0].Name: - self.appendContextMenu("",["Refresh_Path"]) + self.appendContextMenu("", ["Path_Inspect"]) + if "Profile" in FreeCADGui.Selection.getSelection()[0].Name: + self.appendContextMenu("", ["Add_Tag"]) + self.appendContextMenu("", ["Set_StartPoint"]) + self.appendContextMenu("", ["Set_EndPoint"]) + if "Remote" in FreeCADGui.Selection.getSelection()[0].Name: + self.appendContextMenu("", ["Refresh_Path"]) Gui.addWorkbench(PathWorkbench()) -FreeCAD.addImportType("GCode (*.nc *.gc *.ncc *.ngc *.cnc *.tap *.gcode)","PathGui") -FreeCAD.addExportType("GCode (*.nc *.gc *.ncc *.ngc *.cnc *.tap *.gcode)","PathGui") +FreeCAD.addImportType( + "GCode (*.nc *.gc *.ncc *.ngc *.cnc *.tap *.gcode)", "PathGui") +FreeCAD.addExportType( + "GCode (*.nc *.gc *.ncc *.ngc *.cnc *.tap *.gcode)", "PathGui") diff --git a/src/Mod/Path/PathScripts/PathDrilling.py b/src/Mod/Path/PathScripts/PathDrilling.py index 054d42db3c03..4fed01a850de 100644 --- a/src/Mod/Path/PathScripts/PathDrilling.py +++ b/src/Mod/Path/PathScripts/PathDrilling.py @@ -74,7 +74,7 @@ def __setstate__(self, state): def execute(self, obj): output = "" toolLoad = PathUtils.getLastToolLoad(obj) - if toolLoad is None: + if toolLoad is None or toolLoad.ToolNumber == 0: self.vertFeed = 100 self.horizFeed = 100 self.radius = 0.25 diff --git a/src/Mod/Path/PathScripts/PathEngrave.py b/src/Mod/Path/PathScripts/PathEngrave.py index cbbb0e3d66a1..f234925af670 100644 --- a/src/Mod/Path/PathScripts/PathEngrave.py +++ b/src/Mod/Path/PathScripts/PathEngrave.py @@ -80,7 +80,7 @@ def execute(self, obj): output = "" toolLoad = PathUtils.getLastToolLoad(obj) - if toolLoad is None: + if toolLoad is None or toolLoad.ToolNumber == 0: self.vertFeed = 100 self.horizFeed = 100 self.radius = 0.25 diff --git a/src/Mod/Path/PathScripts/PathMachine.py b/src/Mod/Path/PathScripts/PathMachine.py index fcca0cf7d2a7..11523fec1e4d 100644 --- a/src/Mod/Path/PathScripts/PathMachine.py +++ b/src/Mod/Path/PathScripts/PathMachine.py @@ -1,79 +1,99 @@ # -*- coding: utf-8 -*- -#*************************************************************************** -#* * -#* Copyright (c) 2015 Dan Falck * -#* * -#* 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 * -#* * -#*************************************************************************** +# *************************************************************************** +# * * +# * Copyright (c) 2015 Dan Falck * +# * * +# * 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 * +# * * +# *************************************************************************** ''' A CNC machine object to define how code is posted ''' -import FreeCAD,Path +import FreeCAD +import Path import PathScripts -from PathScripts import PathProject, PathUtils -from PySide import QtCore,QtGui -import os, sys +from PathScripts import PathUtils +from PySide import QtCore, QtGui +import os +import sys # Qt tanslation handling try: _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): return QtGui.QApplication.translate(context, text, disambig, _encoding) except AttributeError: def translate(context, text, disambig=None): return QtGui.QApplication.translate(context, text, disambig) -class Machine: - def __init__(self,obj): - - obj.addProperty("App::PropertyString", "MachineName","Base",translate("Machine Name","Name of the Machine that will use the CNC program")) - - obj.addProperty("App::PropertyFile", "PostProcessor", "CodeOutput", translate("Post Processor","Select the Post Processor file for this machine")) - #obj.setEditorMode("PostProcessor",1) #set to read only - obj.addProperty("App::PropertyEnumeration", "MachineUnits","CodeOutput", translate( "Machine Units", "Units that the machine works in, ie Metric or Inch")) - obj.MachineUnits=['Metric', 'Inch'] - - obj.addProperty("Path::PropertyTooltable","Tooltable", "Base",translate("Tool Table","The tooltable used for this CNC program")) - obj.addProperty("App::PropertyDistance", "X_Max", "Limits", translate("X Maximum Limit","The Maximum distance in X the machine can travel")) - obj.addProperty("App::PropertyDistance", "Y_Max", "Limits", translate("Y Maximum Limit","The Maximum distance in X the machine can travel")) - obj.addProperty("App::PropertyDistance", "Z_Max", "Limits", translate("Y Maximum Limit","The Maximum distance in X the machine can travel")) - - obj.addProperty("App::PropertyDistance", "X_Min", "Limits", translate("X Minimum Limit","The Minimum distance in X the machine can travel")) - obj.addProperty("App::PropertyDistance", "Y_Min", "Limits", translate("Y Minimum Limit","The Minimum distance in X the machine can travel")) - obj.addProperty("App::PropertyDistance", "Z_Min", "Limits", translate("Y Minimum Limit","The Minimum distance in X the machine can travel")) +class Machine: - obj.addProperty("App::PropertyDistance", "X", "HomePosition", translate("X Home Position","Home position of machine, in X (mainly for visualization)")) - obj.addProperty("App::PropertyDistance", "Y", "HomePosition", translate("Y Home Position","Home position of machine, in Y (mainly for visualization)")) - obj.addProperty("App::PropertyDistance", "Z", "HomePosition", translate("Z Home Position","Home position of machine, in Z (mainly for visualization)")) + def __init__(self, obj): + + obj.addProperty("App::PropertyString", "MachineName", "Base", translate( + "Machine Name", "Name of the Machine that will use the CNC program")) + + obj.addProperty("App::PropertyFile", "PostProcessor", "CodeOutput", translate( + "Post Processor", "Select the Post Processor file for this machine")) + # obj.setEditorMode("PostProcessor",1) #set to read only + obj.addProperty("App::PropertyEnumeration", "MachineUnits", "CodeOutput", translate( + "Machine Units", "Units that the machine works in, ie Metric or Inch")) + obj.MachineUnits = ['Metric', 'Inch'] + + obj.addProperty("Path::PropertyTooltable", "Tooltable", "Base", translate( + "Tool Table", "The tooltable used for this CNC program")) + + obj.addProperty("App::PropertyDistance", "X_Max", "Limits", translate( + "X Maximum Limit", "The Maximum distance in X the machine can travel")) + obj.addProperty("App::PropertyDistance", "Y_Max", "Limits", translate( + "Y Maximum Limit", "The Maximum distance in X the machine can travel")) + obj.addProperty("App::PropertyDistance", "Z_Max", "Limits", translate( + "Y Maximum Limit", "The Maximum distance in X the machine can travel")) + + obj.addProperty("App::PropertyDistance", "X_Min", "Limits", translate( + "X Minimum Limit", "The Minimum distance in X the machine can travel")) + obj.addProperty("App::PropertyDistance", "Y_Min", "Limits", translate( + "Y Minimum Limit", "The Minimum distance in X the machine can travel")) + obj.addProperty("App::PropertyDistance", "Z_Min", "Limits", translate( + "Y Minimum Limit", "The Minimum distance in X the machine can travel")) + + obj.addProperty("App::PropertyDistance", "X", "HomePosition", translate( + "X Home Position", "Home position of machine, in X (mainly for visualization)")) + obj.addProperty("App::PropertyDistance", "Y", "HomePosition", translate( + "Y Home Position", "Home position of machine, in Y (mainly for visualization)")) + obj.addProperty("App::PropertyDistance", "Z", "HomePosition", translate( + "Z Home Position", "Home position of machine, in Z (mainly for visualization)")) obj.Proxy = self mode = 2 - obj.setEditorMode('Placement',mode) + obj.setEditorMode('Placement', mode) - def execute(self,obj): - obj.Label = "Machine_"+str(obj.MachineName) - gcode = 'G0 X'+str(obj.X.Value)+' Y'+str(obj.Y.Value)+' Z'+str(obj.Z.Value) #need to filter this path out in post- only for visualization + def execute(self, obj): + obj.Label = "Machine_" + str(obj.MachineName) + # need to filter this path out in post- only for visualization + gcode = 'G0 X' + str(obj.X.Value) + ' Y' + \ + str(obj.Y.Value) + ' Z' + str(obj.Z.Value) obj.Path = Path.Path(gcode) - def onChanged(self,obj,prop): + def onChanged(self, obj, prop): mode = 2 - obj.setEditorMode('Placement',mode) + obj.setEditorMode('Placement', mode) if prop == "PostProcessor": sys.path.append(os.path.split(obj.PostProcessor)[0]) @@ -81,161 +101,167 @@ def onChanged(self,obj,prop): postname = os.path.split(lessextn)[1] exec "import %s as current_post" % postname - if hasattr (current_post, "UNITS"): + if hasattr(current_post, "UNITS"): if current_post.UNITS == "G21": obj.MachineUnits = "Metric" else: obj.MachineUnits = "Inch" - if hasattr (current_post, "MACHINE_NAME"): obj.MachineName = current_post.MACHINE_NAME + if hasattr(current_post, "MACHINE_NAME"): + obj.MachineName = current_post.MACHINE_NAME - if hasattr (current_post, "CORNER_MAX"): + if hasattr(current_post, "CORNER_MAX"): obj.X_Max = current_post.CORNER_MAX['x'] obj.Y_Max = current_post.CORNER_MAX['y'] obj.Z_Max = current_post.CORNER_MAX['z'] - if hasattr (current_post, "CORNER_MIN"): + if hasattr(current_post, "CORNER_MIN"): obj.X_Min = current_post.CORNER_MIN['x'] obj.Y_Min = current_post.CORNER_MIN['y'] obj.Z_Min = current_post.CORNER_MIN['z'] if prop == "Tooltable": - proj = PathUtils.findProj() + proj = PathUtils.findProj() for g in proj.Group: if not(isinstance(g.Proxy, PathScripts.PathMachine.Machine)): g.touch() - class _ViewProviderMachine: - def __init__(self,vobj): + + def __init__(self, vobj): vobj.Proxy = self - vobj.addProperty("App::PropertyBool","ShowLimits","Path","Switch the machine max and minimum travel bounding box on/off") + vobj.addProperty("App::PropertyBool", "ShowLimits", "Path", translate( + "ShowMinMaxTravel", "Switch the machine max and minimum travel bounding box on/off")) mode = 2 - vobj.setEditorMode('LineWidth',mode) - vobj.setEditorMode('MarkerColor',mode) - vobj.setEditorMode('NormalColor',mode) - vobj.setEditorMode('ShowFirstRapid',0) - vobj.setEditorMode('DisplayMode',mode) - vobj.setEditorMode('BoundingBox',mode) - vobj.setEditorMode('Selectable',mode) - - - def __getstate__(self): #mandatory + vobj.setEditorMode('LineWidth', mode) + vobj.setEditorMode('MarkerColor', mode) + vobj.setEditorMode('NormalColor', mode) + vobj.setEditorMode('ShowFirstRapid', 0) + vobj.setEditorMode('DisplayMode', mode) + vobj.setEditorMode('BoundingBox', mode) + vobj.setEditorMode('Selectable', mode) + + def __getstate__(self): # mandatory return None - def __setstate__(self,state): #mandatory + def __setstate__(self, state): # mandatory return None - def getIcon(self): #optional + def getIcon(self): # optional return ":/icons/Path-Machine.svg" - def attach(self,vobj): + def attach(self, vobj): from pivy import coin self.extentsBox = coin.SoSeparator() vobj.RootNode.addChild(self.extentsBox) - - def onChanged(self,vobj,prop): + + def onChanged(self, vobj, prop): if prop == "ShowLimits": self.extentsBox.removeAllChildren() - if vobj.ShowLimits and hasattr(vobj,"Object"): + if vobj.ShowLimits and hasattr(vobj, "Object"): from pivy import coin - parent = coin.SoType.fromName("SoSkipBoundingGroup").createInstance() + parent = coin.SoType.fromName( + "SoSkipBoundingGroup").createInstance() self.extentsBox.addChild(parent) # set pattern - pattern = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Part").GetInt("GridLinePattern",0x0f0f) + pattern = FreeCAD.ParamGet( + "User parameter:BaseApp/Preferences/Mod/Part").GetInt("GridLinePattern", 0x0f0f) defStyle = coin.SoDrawStyle() defStyle.lineWidth = 1 defStyle.linePattern = pattern parent.addChild(defStyle) # set color - c = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Path").GetUnsigned("DefaultExtentsColor",3418866943) - r = float((c>>24)&0xFF)/255.0 - g = float((c>>16)&0xFF)/255.0 - b = float((c>>8)&0xFF)/255.0 + c = FreeCAD.ParamGet( + "User parameter:BaseApp/Preferences/Mod/Path").GetUnsigned("DefaultExtentsColor", 3418866943) + r = float((c >> 24) & 0xFF) / 255.0 + g = float((c >> 16) & 0xFF) / 255.0 + b = float((c >> 8) & 0xFF) / 255.0 color = coin.SoBaseColor() parent.addChild(color) # set boundbox - extents = coin.SoType.fromName("SoFCBoundingBox").createInstance() + extents = coin.SoType.fromName( + "SoFCBoundingBox").createInstance() extents.coordsOn.setValue(False) extents.dimensionsOn.setValue(False) - XMax, YMax, ZMax =vobj.Object.X_Max.Value , vobj.Object.Y_Max.Value , vobj.Object.Z_Max.Value - XMin, YMin, ZMin =vobj.Object.X_Min.Value , vobj.Object.Y_Min.Value , vobj.Object.Z_Min.Value - UnitParams = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units") + XMax, YMax, ZMax = vobj.Object.X_Max.Value, vobj.Object.Y_Max.Value, vobj.Object.Z_Max.Value + XMin, YMin, ZMin = vobj.Object.X_Min.Value, vobj.Object.Y_Min.Value, vobj.Object.Z_Min.Value + # UnitParams = FreeCAD.ParamGet( + # "User parameter:BaseApp/Preferences/Units") extents.minBounds.setValue(XMax, YMax, ZMax) extents.maxBounds.setValue(XMin, YMin, ZMin) parent.addChild(extents) mode = 2 - vobj.setEditorMode('LineWidth',mode) - vobj.setEditorMode('MarkerColor',mode) - vobj.setEditorMode('NormalColor',mode) - vobj.setEditorMode('ShowFirstRapid',0) - vobj.setEditorMode('DisplayMode',mode) - vobj.setEditorMode('BoundingBox',mode) - vobj.setEditorMode('Selectable',mode) - - - - - def updateData(self,vobj,prop): #optional + vobj.setEditorMode('LineWidth', mode) + vobj.setEditorMode('MarkerColor', mode) + vobj.setEditorMode('NormalColor', mode) + vobj.setEditorMode('ShowFirstRapid', 0) + vobj.setEditorMode('DisplayMode', mode) + vobj.setEditorMode('BoundingBox', mode) + vobj.setEditorMode('Selectable', mode) + + def updateData(self, vobj, prop): # optional # this is executed when a property of the APP OBJECT changes pass - def setEdit(self,vobj,mode=0): #optional + def setEdit(self, vobj, mode=0): # optional # this is executed when the object is double-clicked in the tree pass - def unsetEdit(self,vobj,mode=0): #optional + def unsetEdit(self, vobj, mode=0): # optional # this is executed when the user cancels or terminates edit mode pass - - def doubleClicked(self,vobj): + + def doubleClicked(self, vobj): from PathScripts import TooltableEditor TooltableEditor.edit(vobj.Object.Name) + class CommandPathMachine: + def GetResources(self): - return {'Pixmap' : 'Path-Machine', - 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathMachine","Machine Object"), + return {'Pixmap': 'Path-Machine', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("PathMachine", "Machine Object"), 'Accel': "P, M", - 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathMachine","Create a Machine object")} + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathMachine", "Create a Machine object")} def IsActive(self): - return not FreeCAD.ActiveDocument is None + return FreeCAD.ActiveDocument is not None def Activated(self): - FreeCAD.ActiveDocument.openTransaction(translate("PathMachine","Create a Machine object")) + FreeCAD.ActiveDocument.openTransaction( + translate("PathMachine", "Create a Machine object")) CommandPathMachine.Create() FreeCAD.ActiveDocument.commitTransaction() FreeCAD.ActiveDocument.recompute() @staticmethod def Create(): - obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython","Machine") + obj = FreeCAD.ActiveDocument.addObject( + "Path::FeaturePython", "Machine") Machine(obj) _ViewProviderMachine(obj.ViewObject) PathUtils.addToProject(obj) - UnitParams = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Units") + UnitParams = FreeCAD.ParamGet( + "User parameter:BaseApp/Preferences/Units") if UnitParams.GetInt('UserSchema') == 0: obj.MachineUnits = 'Metric' - #metric mode + # metric mode else: obj.MachineUnits = 'Inch' obj.ViewObject.ShowFirstRapid = False return obj -if FreeCAD.GuiUp: +if FreeCAD.GuiUp: # register the FreeCAD command import FreeCADGui - FreeCADGui.addCommand('Path_Machine',CommandPathMachine()) + FreeCADGui.addCommand('Path_Machine', CommandPathMachine()) FreeCAD.Console.PrintLog("Loading PathMachine... done\n") - - diff --git a/src/Mod/Path/PathScripts/PathPocket.py b/src/Mod/Path/PathScripts/PathPocket.py index 92d622412da5..b0ca151e06de 100644 --- a/src/Mod/Path/PathScripts/PathPocket.py +++ b/src/Mod/Path/PathScripts/PathPocket.py @@ -74,8 +74,8 @@ def __init__(self, obj): obj.addProperty("App::PropertyDistance", "MaterialAllowance", "Pocket", translate("PathProject", "Amount of material to leave")) obj.addProperty("App::PropertyEnumeration", "StartAt", "Pocket", translate("PathProject", "Start pocketing at center or boundary")) obj.StartAt = ['Center', 'Edge'] - obj.addProperty("App::PropertyFloatConstraint", "StepOver", "Pocket", translate("PathProject", "Amount to step over on each pass")) - obj.StepOver = (0.0, 0.01, 100.0, 0.5) + obj.addProperty("App::PropertyPercent", "StepOver", "Pocket", translate("PathProject", "Percent of cutter diameter to step over on each pass")) + #obj.StepOver = (0.0, 0.01, 100.0, 0.5) obj.addProperty("App::PropertyBool", "KeepToolDown", "Pocket", translate("PathProject", "Attempts to avoid unnecessary retractions.")) obj.addProperty("App::PropertyBool", "ZigUnidirectional", "Pocket", translate("PathProject", "Lifts tool at the end of each pass to respect cut mode.")) obj.addProperty("App::PropertyBool", "UseZigZag", "Pocket", translate("PathProject", "Use Zig Zag pattern to clear area.")) @@ -118,22 +118,7 @@ def addpocketbase(self, obj, ss, sub=""): if baselist is None: baselist = [] if len(baselist) == 0: # When adding the first base object, guess at heights - # try: - # bb = ss.Shape.BoundBox # parent boundbox - # subobj = ss.Shape.getElement(sub) - # fbb = subobj.BoundBox # feature boundbox - # obj.StartDepth = bb.ZMax - # obj.ClearanceHeight = bb.ZMax + 5.0 - # obj.SafeHeight = bb.ZMax + 3.0 - - # if fbb.ZMax < bb.ZMax: - # obj.FinalDepth = fbb.ZMax - # else: - # obj.FinalDepth = bb.ZMin - # except: - # obj.StartDepth = 5.0 - # obj.ClearanceHeight = 10.0 - # obj.SafeHeight = 8.0 + try: bb = ss.Shape.BoundBox # parent boundbox @@ -197,7 +182,7 @@ def buildpathlibarea(self, obj, a): obj.FinalDepth.Value) extraoffset = obj.MaterialAllowance.Value - stepover = obj.StepOver + stepover = (self.radius * 2) * (float(obj.StepOver)/100) use_zig_zag = obj.UseZigZag zig_angle = obj.ZigZagAngle from_center = (obj.StartAt == "Center") @@ -242,22 +227,23 @@ def buildpathocc(self, obj, shape): # Build up the offset loops output = "" offsets = [] - nextradius = self.radius + nextradius = (self.radius * 2) * (float(obj.StepOver)/100) result = DraftGeomUtils.pocket2d(shape, nextradius) print "did we get something: " + str(result) while result: print "Adding " + str(len(result)) + " wires" offsets.extend(result) - nextradius += self.radius + nextradius += (self.radius * 2) * (float(obj.StepOver)/100) result = DraftGeomUtils.pocket2d(shape, nextradius) # revert the list so we start with the outer wires if obj.StartAt != 'Edge': offsets.reverse() + plungePos = None + rampEdge = None if obj.UseEntry: # Try to find an entry location - plungePos = None toold = self.radius*2 helixBounds = DraftGeomUtils.pocket2d(shape, self.radius * (1 + obj.HelixSize)) @@ -279,7 +265,6 @@ def buildpathocc(self, obj, shape): # Maybe reverse helixBounds and pick off that? if plungePos is None: # If we didn't find a place to helix, how about a ramp? - rampEdge = None FreeCAD.Console.PrintMessage(translate("PathPocket", "Attempting ramp entry.\n")) if (offsets[0].Edges[0].Length >= toold * rampD) and not (isinstance(offsets[0].Edges[0].Curve, Part.Circle)): rampEdge = offsets[0].Edges[0] @@ -305,6 +290,7 @@ def buildpathocc(self, obj, shape): first = True # loop over successive wires for currentWire in offsets: + #output += PathUtils.convert(currentWire.Edges, "on", 1) last = None for edge in currentWire.Edges: if not last: @@ -363,7 +349,7 @@ def buildpathocc(self, obj, shape): def execute(self, obj): output = "" toolLoad = PathUtils.getLastToolLoad(obj) - if toolLoad is None: + if toolLoad is None or toolLoad.ToolNumber == 0: self.vertFeed = 100 self.horizFeed = 100 self.radius = 0.25 @@ -371,13 +357,14 @@ def execute(self, obj): else: self.vertFeed = toolLoad.VertFeed.Value self.horizFeed = toolLoad.HorizFeed.Value + tool = PathUtils.getTool(obj, toolLoad.ToolNumber) + self.radius = tool.Diameter/2 obj.ToolNumber = toolLoad.ToolNumber - tool = PathUtils.getTool(obj, toolLoad.ToolNumber) - if tool is None: - self.radius = 0.25 - else: - self.radius = tool.Diameter/2 +# if tool is None: +# self.radius = 0.25 +# else: +# self.radius = tool.Diameter/2 if obj.Base: for b in obj.Base: @@ -514,7 +501,7 @@ def Activated(self): FreeCADGui.doCommand('obj.Active = True') FreeCADGui.doCommand('PathScripts.PathPocket.ViewProviderPocket(obj.ViewObject)') FreeCADGui.doCommand('from PathScripts import PathUtils') - FreeCADGui.doCommand('obj.StepOver = 1.0') + FreeCADGui.doCommand('obj.StepOver = 100') FreeCADGui.doCommand('obj.ClearanceHeight = 10') # + str(bb.ZMax + 2.0)) FreeCADGui.doCommand('obj.StepDown = 1.0') FreeCADGui.doCommand('obj.StartDepth = ' + str(ztop)) diff --git a/src/Mod/Path/PathScripts/PathProfile.py b/src/Mod/Path/PathScripts/PathProfile.py index 76319e0d84ea..226c89e076ae 100644 --- a/src/Mod/Path/PathScripts/PathProfile.py +++ b/src/Mod/Path/PathScripts/PathProfile.py @@ -258,7 +258,7 @@ def execute(self, obj): import Part # math #DraftGeomUtils output = "" toolLoad = PathUtils.getLastToolLoad(obj) - if toolLoad is None: + if toolLoad is None or toolLoad.ToolNumber == 0: self.vertFeed = 100 self.horizFeed = 100 self.radius = 0.25 diff --git a/src/Mod/Path/PathScripts/PathRemote.py b/src/Mod/Path/PathScripts/PathRemote.py index 961a825b4cc7..57e01fde5443 100644 --- a/src/Mod/Path/PathScripts/PathRemote.py +++ b/src/Mod/Path/PathScripts/PathRemote.py @@ -157,7 +157,7 @@ def execute(self, obj): output = "" toolLoad = PathUtils.getLastToolLoad(obj) - if toolLoad is None: + if toolLoad is None or toolLoad.ToolNumber == 0: self.vertFeed = 100 self.horizFeed = 100 self.radius = 0.25 diff --git a/src/Mod/Path/PathScripts/PathSanity.py b/src/Mod/Path/PathScripts/PathSanity.py new file mode 100644 index 000000000000..bf8b84222412 --- /dev/null +++ b/src/Mod/Path/PathScripts/PathSanity.py @@ -0,0 +1,100 @@ +# *************************************************************************** +# * (c) Sliptonic (shopinthewoods@gmail.com) 2016 * +# * * +# * This file is part of the FreeCAD CAx development system. * +# * * +# * 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. * +# * * +# * FreeCAD 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 Lesser General Public License for more details. * +# * * +# * You should have received a copy of the GNU Library General Public * +# * License along with FreeCAD; if not, write to the Free Software * +# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * +# * USA * +# * * +# ***************************************************************************/ + +'''This file has utilities for checking and catching common errors in FreeCAD +Path projects. Ideally, the user could execute these utilities from an icon +to make sure tools are selected and configured and defaults have been revised''' + +from PySide import QtCore, QtGui +import FreeCAD +import FreeCADGui +import PathScripts.PathUtils as PU + +# Qt tanslation handling +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def translate(context, text, disambig=None): + return QtGui.QApplication.translate(context, text, disambig) + + +def review(obj): + + "checks the selected project for common errors" + for item in obj.Group: + print "Checking: " + item.Label + + if item.Name[:4] == "Tool": + if item.ToolNumber == 0: + FreeCAD.Console.PrintWarning(translate("Path_Sanity", "Tool Controller: " + str(item.Label) + " is using ID 0 which is the default\n")) + else: + tool = PU.getTool(item, item.ToolNumber) + if tool is None: + FreeCAD.Console.PrintError(translate("Path_Sanity", "Tool Controller: " + str(item.Label) + " is using tool: " + str(item.ToolNumber) + " which is invalid\n")) + continue + + if tool.Diameter == 0: + FreeCAD.Console.PrintError(translate("Path_Sanity", "Tool Controller: " + str(item.Label) + " is using tool: " + str(item.ToolNumber) + " which has a zero diameter\n")) + if item.HorizFeed == 0: + FreeCAD.Console.PrintWarning(translate("Path_Sanity", "Tool Controller: " + str(item.Label) + " has a 0 value for the Horizontal feed rate\n")) + if item.VertFeed == 0: + FreeCAD.Console.PrintWarning(translate("Path_Sanity", "Tool Controller: " + str(item.Label) + " has a 0 value for the Vertical feed rate\n")) + if item.SpindleSpeed == 0: + FreeCAD.Console.PrintWarning(translate("Path_Sanity", "Tool Controller: " + str(item.Label) + " has a 0 value for the spindle speed\n")) + + if item.Name[:7] == "Machine": + if len(item.Tooltable.Tools) == 0: + FreeCAD.Console.PrintWarning(translate("Path_Sanity", "Machine: " + str(item.Label) + " has no tools defined in the tool table\n")) + + +class CommandPathSanity: + + def GetResources(self): + return {'Pixmap' : 'Path-Sanity', + 'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_Sanity","Check the Path project for common errors"), + 'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_Sanity","Check the Path Project for common errors")} + + def IsActive(self): + return not FreeCAD.ActiveDocument is None + + def Activated(self): + # check that the selection contains exactly what we want + selection = FreeCADGui.Selection.getSelection() + if len(selection) != 1: + FreeCAD.Console.PrintError(translate("Path_Sanity","Please select a path Project to check\n")) + return + if not(selection[0].TypeId == "Path::FeatureCompoundPython"): + FreeCAD.Console.PrintError(translate("Path_Sanity","Please select a path project to check\n")) + return + + # if everything is ok, execute + FreeCADGui.addModule("PathScripts.PathSanity") + FreeCADGui.doCommand('PathScripts.PathSanity.review(FreeCAD.ActiveDocument.' + selection[0].Name + ')') + + +if FreeCAD.GuiUp: + # register the FreeCAD command + FreeCADGui.addCommand('Path_Sanity',CommandPathSanity()) + diff --git a/src/Mod/Path/PathScripts/PathSurface.py b/src/Mod/Path/PathScripts/PathSurface.py index e9d44878ce95..403795f3f792 100644 --- a/src/Mod/Path/PathScripts/PathSurface.py +++ b/src/Mod/Path/PathScripts/PathSurface.py @@ -241,7 +241,7 @@ def execute(self,obj): output = "" toolLoad = PathUtils.getLastToolLoad(obj) - if toolLoad == None: + if toolLoad == None or toolLoad.ToolNumber == 0: self.vertFeed = 100 self.horizFeed = 100 self.radius = 0.25 diff --git a/src/Mod/Path/PathScripts/PathUtils.py b/src/Mod/Path/PathScripts/PathUtils.py index 0816b792a153..937bf7361b0a 100644 --- a/src/Mod/Path/PathScripts/PathUtils.py +++ b/src/Mod/Path/PathScripts/PathUtils.py @@ -26,6 +26,7 @@ import Part import math from DraftGeomUtils import geomType +from DraftGeomUtils import findWires import DraftVecUtils import PathScripts from PathScripts import PathProject @@ -271,9 +272,11 @@ def SortPath(wire, Side, radius, clockwise, firstedge=None, SegLen=0.5): sortedpreoff = Part.__sortEdges__(preoffset) wire = Part.Wire(sortedpreoff) + #wire = findWires(sortedpreoff)[0] else: sortedpreoff = Part.__sortEdges__(edgelist) wire = Part.Wire(sortedpreoff) + #wire = findWires(sortedpreoff)[0] edgelist = [] for e in wire.Edges: @@ -287,7 +290,9 @@ def SortPath(wire, Side, radius, clockwise, firstedge=None, SegLen=0.5): geomType(e) == "BezierCurve" or \ geomType(e) == "Ellipse": edgelist.append(Part.Wire(curvetowire(e, (SegLen)))) - newwire = Part.Wire(edgelist) + #newwire = Part.Wire(edgelist) + sortededges = Part.__sortEdges__(edgelist) + newwire = findWires(sortededges)[0] if Side == 'Left': # we use the OCC offset feature