423 lines
15 KiB
Python
423 lines
15 KiB
Python
# SPDX-License-Identifier: LGPL-2.1-or-later
|
|
|
|
# ***************************************************************************
|
|
# * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
|
|
# * *
|
|
# * 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 *
|
|
# * *
|
|
# ***************************************************************************
|
|
import FreeCAD
|
|
|
|
if FreeCAD.GuiUp:
|
|
import FreeCADGui
|
|
from FreeCADGui import Workbench
|
|
else:
|
|
# Provide a dummy Workbench class when GUI is not available
|
|
class Workbench:
|
|
pass
|
|
|
|
|
|
FreeCAD.__unit_test__ += ["TestCAMGui"]
|
|
|
|
|
|
class PathCommandGroup:
|
|
def __init__(self, cmdlist, menu, tooltip=None):
|
|
self.cmdlist = cmdlist
|
|
self.menu = menu
|
|
if tooltip is None:
|
|
self.tooltip = menu
|
|
else:
|
|
self.tooltip = tooltip
|
|
|
|
def GetCommands(self):
|
|
return tuple(self.cmdlist)
|
|
|
|
def GetResources(self):
|
|
return {"MenuText": self.menu, "ToolTip": self.tooltip}
|
|
|
|
def IsActive(self):
|
|
if FreeCAD.ActiveDocument is not None:
|
|
for o in FreeCAD.ActiveDocument.Objects:
|
|
if o.Name[:3] == "Job":
|
|
return True
|
|
return False
|
|
|
|
|
|
class CAMWorkbench(Workbench):
|
|
"CAM workbench"
|
|
|
|
def __init__(self):
|
|
self.__class__.Icon = FreeCAD.getResourceDir() + "Mod/CAM/Resources/icons/CAMWorkbench.svg"
|
|
self.__class__.MenuText = "CAM"
|
|
self.__class__.ToolTip = "CAM workbench"
|
|
|
|
def Initialize(self):
|
|
global PathCommandGroup
|
|
|
|
# Add preferences pages - before loading PathGui to properly order pages of Path group
|
|
import Path.Dressup.Gui.Preferences as PathPreferencesPathDressup
|
|
import Path.Tool.assets.ui.preferences as AssetPreferences
|
|
import Path.Main.Gui.PreferencesJob as PathPreferencesPathJob
|
|
|
|
translate = FreeCAD.Qt.translate
|
|
|
|
# load the builtin modules
|
|
import Path
|
|
import PathScripts
|
|
import PathGui
|
|
from PySide import QtCore, QtGui
|
|
|
|
FreeCADGui.addLanguagePath(":/translations")
|
|
FreeCADGui.addIconPath(":/icons")
|
|
import Path.GuiInit
|
|
|
|
from Path.Main.Gui import JobCmd as PathJobCmd
|
|
from Path.Main.Gui import SanityCmd as SanityCmd
|
|
from Path.Tool.toolbit.ui import cmd as PathToolBitCmd
|
|
from Path.Tool.library.ui import cmd as PathToolBitLibraryCmd
|
|
|
|
from Path.Tool.camassets import cam_assets
|
|
|
|
cam_assets.setup()
|
|
|
|
# Check if CAM asset migration is needed for version upgrade
|
|
from Path.Tool.migration.migration import CAMAssetMigrator
|
|
|
|
migrator = CAMAssetMigrator()
|
|
migrator.check_migration_needed()
|
|
|
|
from PySide.QtCore import QT_TRANSLATE_NOOP
|
|
|
|
import PathCommands
|
|
import subprocess
|
|
from packaging.version import Version, parse
|
|
|
|
FreeCADGui.addPreferencePage(
|
|
PathPreferencesPathJob.JobPreferencesPage,
|
|
QT_TRANSLATE_NOOP("QObject", "CAM"),
|
|
)
|
|
FreeCADGui.addPreferencePage(
|
|
AssetPreferences.AssetPreferencesPage,
|
|
QT_TRANSLATE_NOOP("QObject", "CAM"),
|
|
)
|
|
FreeCADGui.addPreferencePage(
|
|
PathPreferencesPathDressup.DressupPreferencesPage,
|
|
QT_TRANSLATE_NOOP("QObject", "CAM"),
|
|
)
|
|
|
|
Path.GuiInit.Startup()
|
|
|
|
# build commands list
|
|
projcmdlist = ["CAM_Job", "CAM_Sanity"]
|
|
postcmdlist = ["CAM_Post", "CAM_PostSelected"]
|
|
toolcmdlist = ["CAM_Inspect", "CAM_SelectLoop", "CAM_OpActiveToggle"]
|
|
|
|
simcmdlist = ["CAM_SimulatorGL", "CAM_Simulator"]
|
|
prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/CAM")
|
|
simLegacy = prefs.GetBool("DefaultSimulatorLegacy", False)
|
|
if simLegacy:
|
|
simcmdlist.reverse()
|
|
|
|
prepcmdlist = [
|
|
"CAM_Comment",
|
|
"CAM_Stop",
|
|
"CAM_Custom",
|
|
"CAM_Probe",
|
|
]
|
|
twodopcmdlist = [
|
|
"CAM_Profile",
|
|
"CAM_Pocket_Shape",
|
|
"CAM_MillFace",
|
|
"CAM_Helix",
|
|
"CAM_Adaptive",
|
|
"CAM_Slot",
|
|
]
|
|
threedopcmdlist = ["CAM_Pocket3D"]
|
|
engravecmdlist = ["CAM_Engrave", "CAM_Deburr", "CAM_Vcarve"]
|
|
drillingcmdlist = ["CAM_Drilling"]
|
|
modcmdlist = ["CAM_OperationCopy", "CAM_Array", "CAM_SimpleCopy"]
|
|
dressupcmdlist = [
|
|
"CAM_DressupArray",
|
|
"CAM_DressupAxisMap",
|
|
"CAM_DressupPathBoundary",
|
|
"CAM_DressupDogbone",
|
|
"CAM_DressupDragKnife",
|
|
"CAM_DressupLeadInOut",
|
|
"CAM_DressupRampEntry",
|
|
"CAM_DressupTag",
|
|
"CAM_DressupZCorrect",
|
|
]
|
|
extracmdlist = []
|
|
specialcmdlist = []
|
|
|
|
toolcmdlist.extend(PathToolBitLibraryCmd.BarList)
|
|
toolbitcmdlist = PathToolBitLibraryCmd.MenuList
|
|
|
|
postcmdgroup = ["CAM_PostTools"]
|
|
FreeCADGui.addCommand(
|
|
"CAM_PostTools",
|
|
PathCommandGroup(
|
|
postcmdlist,
|
|
QT_TRANSLATE_NOOP("CAM_PostTools", "Post process Operations"),
|
|
),
|
|
)
|
|
simcmdgroup = ["CAM_SimTools"]
|
|
FreeCADGui.addCommand(
|
|
"CAM_SimTools",
|
|
PathCommandGroup(
|
|
simcmdlist,
|
|
QT_TRANSLATE_NOOP("CAM_SimTools", "Simulators"),
|
|
),
|
|
)
|
|
engravecmdgroup = ["CAM_EngraveTools"]
|
|
FreeCADGui.addCommand(
|
|
"CAM_EngraveTools",
|
|
PathCommandGroup(
|
|
engravecmdlist,
|
|
QT_TRANSLATE_NOOP("CAM_EngraveTools", "Engraving Operations"),
|
|
),
|
|
)
|
|
if Path.Preferences.experimentalFeaturesEnabled():
|
|
drillingcmdlist.append("CAM_Tapping")
|
|
|
|
if set(["CAM_Drilling", "CAM_Tapping"]).issubset(drillingcmdlist):
|
|
drillingcmdgroup = ["CAM_DrillingTools"]
|
|
FreeCADGui.addCommand(
|
|
"CAM_DrillingTools",
|
|
PathCommandGroup(
|
|
drillingcmdlist,
|
|
QT_TRANSLATE_NOOP("CAM_DrillingTools", "Drilling Operations"),
|
|
),
|
|
)
|
|
else:
|
|
drillingcmdgroup = drillingcmdlist
|
|
|
|
dressupcmdgroup = ["CAM_DressupTools"]
|
|
FreeCADGui.addCommand(
|
|
"CAM_DressupTools",
|
|
PathCommandGroup(
|
|
dressupcmdlist,
|
|
QT_TRANSLATE_NOOP("CAM_DressupTools", "Dressup Operations"),
|
|
),
|
|
)
|
|
threedcmdgroup = threedopcmdlist
|
|
if Path.Preferences.experimentalFeaturesEnabled():
|
|
prepcmdlist.append("CAM_PathShapeTC")
|
|
extracmdlist.extend(["CAM_Area", "CAM_Area_Workplane"])
|
|
specialcmdlist.append("CAM_ThreadMilling")
|
|
twodopcmdlist.append("CAM_Slot")
|
|
|
|
if Path.Preferences.advancedOCLFeaturesEnabled():
|
|
try:
|
|
r = subprocess.run(
|
|
["camotics", "--version"], capture_output=True, text=True
|
|
).stderr.strip()
|
|
v = parse(r)
|
|
|
|
if v >= Version("1.2.2"):
|
|
toolcmdlist.append("CAM_Camotics")
|
|
except (FileNotFoundError, ModuleNotFoundError):
|
|
pass
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"Failed to execute camotics command: {e}")
|
|
except ValueError as ve:
|
|
print(f"Version error: {ve}")
|
|
except Exception as ex:
|
|
print(f"An unexpected error occurred: {ex}")
|
|
|
|
try:
|
|
try:
|
|
import ocl # pylint: disable=unused-variable
|
|
except ImportError:
|
|
import opencamlib as ocl
|
|
from Path.Op.Gui import Surface
|
|
from Path.Op.Gui import Waterline
|
|
|
|
threedopcmdlist.extend(["CAM_Surface", "CAM_Waterline"])
|
|
threedcmdgroup = ["CAM_3dTools"]
|
|
FreeCADGui.addCommand(
|
|
"CAM_3dTools",
|
|
PathCommandGroup(
|
|
threedopcmdlist,
|
|
QT_TRANSLATE_NOOP("CAM_3dTools", "3D Operations"),
|
|
),
|
|
)
|
|
except ImportError:
|
|
if not Path.Preferences.suppressOpenCamLibWarning():
|
|
FreeCAD.Console.PrintError("OpenCamLib is not working!\n")
|
|
|
|
self.appendToolbar(
|
|
QT_TRANSLATE_NOOP("Workbench", "Project Setup"),
|
|
projcmdlist + postcmdgroup,
|
|
)
|
|
self.appendToolbar(
|
|
QT_TRANSLATE_NOOP("Workbench", "Tool Commands"),
|
|
simcmdgroup + toolcmdlist,
|
|
)
|
|
self.appendToolbar(
|
|
QT_TRANSLATE_NOOP("Workbench", "New Operations"),
|
|
twodopcmdlist + drillingcmdgroup + engravecmdgroup + threedcmdgroup,
|
|
)
|
|
self.appendToolbar(
|
|
QT_TRANSLATE_NOOP("Workbench", "Path Modification"), modcmdlist + dressupcmdgroup
|
|
)
|
|
if extracmdlist:
|
|
self.appendToolbar(QT_TRANSLATE_NOOP("Workbench", "Helpful Tools"), extracmdlist)
|
|
|
|
self.appendMenu(
|
|
[QT_TRANSLATE_NOOP("Workbench", "&CAM")],
|
|
projcmdlist
|
|
+ postcmdlist
|
|
+ ["CAM_ExportTemplate", "Separator"]
|
|
+ simcmdlist
|
|
+ toolcmdlist
|
|
+ toolbitcmdlist
|
|
+ ["Separator"]
|
|
+ twodopcmdlist
|
|
+ drillingcmdlist
|
|
+ engravecmdlist
|
|
+ ["Separator"]
|
|
+ threedopcmdlist
|
|
+ ["Separator"],
|
|
)
|
|
self.appendMenu(
|
|
[
|
|
QT_TRANSLATE_NOOP("Workbench", "&CAM"),
|
|
QT_TRANSLATE_NOOP("Workbench", "Path Dressup"),
|
|
],
|
|
dressupcmdlist,
|
|
)
|
|
self.appendMenu(
|
|
[
|
|
QT_TRANSLATE_NOOP("Workbench", "&CAM"),
|
|
QT_TRANSLATE_NOOP("Workbench", "Supplemental Commands"),
|
|
],
|
|
prepcmdlist,
|
|
)
|
|
self.appendMenu(
|
|
[
|
|
QT_TRANSLATE_NOOP("Workbench", "&CAM"),
|
|
QT_TRANSLATE_NOOP("Workbench", "Path Modification"),
|
|
],
|
|
modcmdlist,
|
|
)
|
|
if specialcmdlist:
|
|
self.appendMenu(
|
|
[
|
|
QT_TRANSLATE_NOOP("Workbench", "&CAM"),
|
|
QT_TRANSLATE_NOOP("Workbench", "Specialty Operations"),
|
|
],
|
|
specialcmdlist,
|
|
)
|
|
if extracmdlist:
|
|
self.appendMenu([QT_TRANSLATE_NOOP("Workbench", "&CAM")], extracmdlist)
|
|
|
|
self.appendMenu([QT_TRANSLATE_NOOP("Workbench", "&CAM")], ["Separator"])
|
|
self.appendMenu(
|
|
[
|
|
QT_TRANSLATE_NOOP("Workbench", "&CAM"),
|
|
QT_TRANSLATE_NOOP("Workbench", "Utils"),
|
|
],
|
|
["CAM_PropertyBag"],
|
|
)
|
|
|
|
self.dressupcmds = dressupcmdlist
|
|
|
|
curveAccuracy = Path.Preferences.defaultLibAreaCurveAccuracy()
|
|
if curveAccuracy:
|
|
Path.Area.setDefaultParams(Accuracy=curveAccuracy)
|
|
|
|
# keep this one the last entry in the preferences
|
|
import Path.Base.Gui.PreferencesAdvanced as PathPreferencesAdvanced
|
|
from Path.Preferences import preferences
|
|
|
|
FreeCADGui.addPreferencePage(
|
|
PathPreferencesAdvanced.AdvancedPreferencesPage,
|
|
QT_TRANSLATE_NOOP("QObject", "CAM"),
|
|
)
|
|
Log("Loading CAM workbench... done\n")
|
|
|
|
def GetClassName(self):
|
|
return "Gui::PythonWorkbench"
|
|
|
|
def Activated(self):
|
|
# update the translation engine
|
|
FreeCADGui.updateLocale()
|
|
# Msg("CAM workbench activated\n")
|
|
|
|
def Deactivated(self):
|
|
# Msg("CAM workbench deactivated\n")
|
|
pass
|
|
|
|
def ContextMenu(self, recipient):
|
|
import PathScripts
|
|
|
|
menuAppended = False
|
|
selection = FreeCADGui.Selection.getSelection()
|
|
if len(selection) == 1:
|
|
obj = selection[0]
|
|
selectedName = obj.Name
|
|
if obj.isDerivedFrom("Path::Feature"):
|
|
self.appendContextMenu("", "Separator")
|
|
self.appendContextMenu("", ["CAM_Inspect"])
|
|
if "Remote" in selectedName:
|
|
self.appendContextMenu("", ["Refresh_Path"])
|
|
if "Job" in selectedName:
|
|
self.appendContextMenu("", ["CAM_ExportTemplate"])
|
|
menuAppended = True
|
|
if isinstance(obj.Proxy, Path.Op.Base.ObjectOp):
|
|
self.appendContextMenu("", ["CAM_OperationCopy", "CAM_OpActiveToggle"])
|
|
if hasattr(obj, "StartPoint"):
|
|
self.appendContextMenu("", ["CAM_SetStartPoint"])
|
|
menuAppended = True
|
|
if obj.isDerivedFrom("Path::Feature"):
|
|
if (
|
|
"Profile" in selectedName
|
|
or "Contour" in selectedName
|
|
or "Dressup" in selectedName
|
|
or "Pocket" in selectedName
|
|
):
|
|
self.appendContextMenu("", "Separator")
|
|
# self.appendContextMenu("", ["Set_StartPoint"])
|
|
# self.appendContextMenu("", ["Set_EndPoint"])
|
|
for cmd in self.dressupcmds:
|
|
self.appendContextMenu("Dressup", [cmd])
|
|
menuAppended = True
|
|
if isinstance(obj.Proxy, Path.Tool.ToolBit):
|
|
self.appendContextMenu("", ["CAM_ToolBitSave", "CAM_ToolBitSaveAs"])
|
|
menuAppended = True
|
|
|
|
if selection:
|
|
for obj in selection:
|
|
if not obj.isDerivedFrom("Path::Feature"):
|
|
break
|
|
else:
|
|
self.appendContextMenu("", ["CAM_Post", "CAM_PostSelected"])
|
|
|
|
if menuAppended:
|
|
self.appendContextMenu("", "Separator")
|
|
|
|
|
|
Gui.addWorkbench(CAMWorkbench())
|
|
|
|
FreeCAD.addImportType(
|
|
"GCode (*.nc *.NC *.gc *.GC *.ncc *.NCC *.ngc *.NGC *.cnc *.CNC *.tap *.TAP *.gcode *.GCODE)",
|
|
"PathGui",
|
|
)
|