378 lines
12 KiB
Python
378 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
# ***************************************************************************
|
|
# * Copyright (c) 2014 Yorik van Havre <yorik@uncreated.net> *
|
|
# * *
|
|
# * 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 Path
|
|
import glob
|
|
import os
|
|
import pathlib
|
|
from collections import defaultdict
|
|
from typing import Optional
|
|
|
|
|
|
if True:
|
|
Path.Log.setLevel(Path.Log.Level.DEBUG, Path.Log.thisModule())
|
|
Path.Log.trackModule(Path.Log.thisModule())
|
|
else:
|
|
Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule())
|
|
|
|
|
|
translate = FreeCAD.Qt.translate
|
|
|
|
PreferencesGroup = "User parameter:BaseApp/Preferences/Mod/CAM"
|
|
|
|
DefaultFilePath = "DefaultFilePath"
|
|
DefaultJobTemplate = "DefaultJobTemplate"
|
|
DefaultStockTemplate = "DefaultStockTemplate"
|
|
DefaultTaskPanelLayout = "DefaultTaskPanelLayout"
|
|
|
|
PostProcessorDefault = "PostProcessorDefault"
|
|
PostProcessorDefaultArgs = "PostProcessorDefaultArgs"
|
|
PostProcessorBlacklist = "PostProcessorBlacklist"
|
|
PostProcessorOutputFile = "PostProcessorOutputFile"
|
|
PostProcessorOutputPolicy = "PostProcessorOutputPolicy"
|
|
|
|
ToolGroup = PreferencesGroup + "/Tools"
|
|
ToolPath = "ToolPath"
|
|
LastToolLibrary = "LastToolLibrary"
|
|
LastToolLibrarySortKey = "LastToolLibrarySortKey"
|
|
|
|
# Linear tolerance to use when generating Paths, eg when tessellating geometry
|
|
GeometryTolerance = "GeometryTolerance"
|
|
LibAreaCurveAccuracy = "LibAreaCurveAccuracy"
|
|
|
|
WarningSuppressRapidSpeeds = "WarningSuppressRapidSpeeds"
|
|
WarningSuppressAllSpeeds = "WarningSuppressAllSpeeds"
|
|
WarningSuppressSelectionMode = "WarningSuppressSelectionMode"
|
|
WarningSuppressOpenCamLib = "WarningSuppressOpenCamLib"
|
|
WarningSuppressVelocity = "WarningSuppressVelocity"
|
|
EnableExperimentalFeatures = "EnableExperimentalFeatures"
|
|
EnableAdvancedOCLFeatures = "EnableAdvancedOCLFeatures"
|
|
|
|
|
|
_observers = defaultdict(list) # maps group name to callback functions
|
|
|
|
|
|
def _add_group_observer(group, callback):
|
|
"""Add an observer for any changes on the given parameter group"""
|
|
_observers[group].append(callback)
|
|
|
|
|
|
def _emit_change(group, *args):
|
|
for cb in _observers[group]:
|
|
cb(group, *args)
|
|
|
|
|
|
def preferences():
|
|
return FreeCAD.ParamGet(PreferencesGroup)
|
|
|
|
|
|
def tool_preferences():
|
|
return FreeCAD.ParamGet(ToolGroup)
|
|
|
|
|
|
def addToolPreferenceObserver(callback):
|
|
_add_group_observer(ToolGroup, callback)
|
|
|
|
|
|
def pathPostSourcePath():
|
|
return os.path.join(FreeCAD.getHomePath(), "Mod/CAM/Path/Post/")
|
|
|
|
|
|
def getBuiltinAssetPath() -> pathlib.Path:
|
|
home = pathlib.Path(FreeCAD.getHomePath())
|
|
return home / "Mod" / "CAM" / "Tools"
|
|
|
|
|
|
def getBuiltinLibraryPath() -> pathlib.Path:
|
|
return getBuiltinAssetPath() / "Library"
|
|
|
|
|
|
def getBuiltinShapePath() -> pathlib.Path:
|
|
return getBuiltinAssetPath() / "Shape"
|
|
|
|
|
|
def getBuiltinToolBitPath() -> pathlib.Path:
|
|
return getBuiltinAssetPath() / "Bit"
|
|
|
|
|
|
def getDefaultAssetPath() -> Path:
|
|
data_dir = pathlib.Path(FreeCAD.getUserAppDataDir())
|
|
asset_path = data_dir / "CamAssets"
|
|
asset_path.mkdir(parents=True, exist_ok=True)
|
|
return asset_path
|
|
|
|
|
|
def getAssetPath() -> pathlib.Path:
|
|
pref = tool_preferences()
|
|
|
|
# Check if we have a CamAssets path already set
|
|
cam_assets_path = pref.GetString(ToolPath, "")
|
|
if cam_assets_path:
|
|
# Use mostRecentConfigFromBase to get the most recent versioned path
|
|
most_recent_path = FreeCAD.ApplicationDirectories.mostRecentConfigFromBase(cam_assets_path)
|
|
return pathlib.Path(most_recent_path)
|
|
|
|
# Migration: Check for legacy DefaultFilePath and use it for CamAssets
|
|
legacy_path = defaultFilePath()
|
|
if legacy_path:
|
|
legacy_path_obj = pathlib.Path(legacy_path)
|
|
if legacy_path_obj.exists() and legacy_path_obj.is_dir():
|
|
# Migrate: Set the legacy path as the new CamAssets path
|
|
setAssetPath(legacy_path_obj)
|
|
# Return the most recent version of the legacy path
|
|
most_recent_legacy = FreeCAD.ApplicationDirectories.mostRecentConfigFromBase(
|
|
str(legacy_path_obj)
|
|
)
|
|
return pathlib.Path(most_recent_legacy)
|
|
|
|
# Fallback to default if no legacy path found
|
|
default = getDefaultAssetPath()
|
|
# Return the most recent version of the default path
|
|
most_recent_default = FreeCAD.ApplicationDirectories.mostRecentConfigFromBase(str(default))
|
|
return pathlib.Path(most_recent_default)
|
|
|
|
|
|
def setAssetPath(path: pathlib.Path):
|
|
Path.Log.info(f"Setting asset path to {path}")
|
|
assert path.is_dir(), f"Cannot put a non-initialized asset directory into preferences: {path}"
|
|
pref = tool_preferences()
|
|
current_path = pref.GetString(ToolPath, "")
|
|
if str(path) == current_path:
|
|
return
|
|
pref.SetString(ToolPath, str(path))
|
|
_emit_change(ToolGroup, ToolPath, path)
|
|
|
|
|
|
def getToolBitPath() -> pathlib.Path:
|
|
return getAssetPath() / "Tools" / "Bit"
|
|
|
|
|
|
def getTemplateDirectory() -> pathlib.Path:
|
|
"""Returns the directory where job templates should be saved."""
|
|
template_path = getAssetPath() / "Templates"
|
|
template_path.mkdir(parents=True, exist_ok=True)
|
|
return template_path
|
|
|
|
|
|
def getLastToolLibrary() -> Optional[str]:
|
|
pref = tool_preferences()
|
|
return pref.GetString(LastToolLibrary) or None
|
|
|
|
|
|
def setLastToolLibrary(name: str):
|
|
assert isinstance(name, str), f"Library name '{name}' is not a string"
|
|
pref = tool_preferences()
|
|
pref.SetString(LastToolLibrary, name)
|
|
|
|
|
|
def getLastToolLibrarySortKey() -> Optional[str]:
|
|
pref = tool_preferences()
|
|
return pref.GetString(LastToolLibrarySortKey) or None
|
|
|
|
|
|
def setLastToolLibrarySortKey(name: str):
|
|
pref = tool_preferences()
|
|
pref.SetString(LastToolLibrarySortKey, name)
|
|
|
|
|
|
def allAvailablePostProcessors():
|
|
allposts = []
|
|
for path in searchPathsPost():
|
|
posts = [
|
|
str(os.path.split(os.path.splitext(p)[0])[1][:-5])
|
|
for p in glob.glob(path + "/*_post.py")
|
|
]
|
|
allposts.extend(posts)
|
|
allposts.sort()
|
|
return allposts
|
|
|
|
|
|
def allEnabledPostProcessors(include=None):
|
|
blacklist = postProcessorBlacklist()
|
|
enabled = [
|
|
processor for processor in allAvailablePostProcessors() if processor not in blacklist
|
|
]
|
|
if include:
|
|
postlist = list(set(include + enabled))
|
|
postlist.sort()
|
|
return postlist
|
|
return enabled
|
|
|
|
|
|
def defaultPostProcessor():
|
|
pref = preferences()
|
|
return pref.GetString(PostProcessorDefault, "")
|
|
|
|
|
|
def defaultPostProcessorArgs():
|
|
pref = preferences()
|
|
return pref.GetString(PostProcessorDefaultArgs, "")
|
|
|
|
|
|
def defaultGeometryTolerance():
|
|
return preferences().GetFloat(GeometryTolerance, 0.01)
|
|
|
|
|
|
def defaultLibAreaCurveAccuracy():
|
|
return preferences().GetFloat(LibAreaCurveAccuracy, 0.01)
|
|
|
|
|
|
def defaultFilePath():
|
|
return preferences().GetString(DefaultFilePath)
|
|
|
|
|
|
def filePath():
|
|
path = defaultFilePath()
|
|
if not path:
|
|
path = getAssetPath()
|
|
return path
|
|
|
|
|
|
def macroFilePath():
|
|
grp = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Macro")
|
|
return grp.GetString("MacroPath", FreeCAD.getUserMacroDir())
|
|
|
|
|
|
def searchPaths():
|
|
paths = []
|
|
# Add new CamAssets/Templates directory first (highest priority)
|
|
paths.append(str(getTemplateDirectory()))
|
|
# Add legacy locations for backward compatibility
|
|
p = defaultFilePath()
|
|
if p:
|
|
paths.append(p)
|
|
paths.append(macroFilePath())
|
|
return paths
|
|
|
|
|
|
def searchPathsPost():
|
|
paths = []
|
|
p = defaultFilePath()
|
|
if p:
|
|
paths.append(p)
|
|
paths.append(macroFilePath())
|
|
paths.append(os.path.join(pathPostSourcePath(), "scripts/"))
|
|
paths.append(pathPostSourcePath())
|
|
return paths
|
|
|
|
|
|
def defaultJobTemplate():
|
|
template = preferences().GetString(DefaultJobTemplate)
|
|
if "xml" not in template:
|
|
return template
|
|
return ""
|
|
|
|
|
|
def setJobDefaults(jobTemplate, geometryTolerance, curveAccuracy):
|
|
Path.Log.track("(%s, %s, %s)" % (jobTemplate, geometryTolerance, curveAccuracy))
|
|
pref = preferences()
|
|
pref.SetString(DefaultJobTemplate, jobTemplate)
|
|
pref.SetFloat(GeometryTolerance, geometryTolerance)
|
|
pref.SetFloat(LibAreaCurveAccuracy, curveAccuracy)
|
|
|
|
|
|
def postProcessorBlacklist():
|
|
pref = preferences()
|
|
blacklist = pref.GetString(PostProcessorBlacklist, "")
|
|
if not blacklist:
|
|
return []
|
|
return eval(blacklist)
|
|
|
|
|
|
def setPostProcessorDefaults(processor, args, blacklist):
|
|
pref = preferences()
|
|
pref.SetString(PostProcessorDefault, processor)
|
|
pref.SetString(PostProcessorDefaultArgs, args)
|
|
pref.SetString(PostProcessorBlacklist, "%s" % (blacklist))
|
|
|
|
|
|
def setOutputFileDefaults(fileName, policy):
|
|
pref = preferences()
|
|
pref.SetString(PostProcessorOutputFile, fileName)
|
|
pref.SetString(PostProcessorOutputPolicy, policy)
|
|
|
|
|
|
def defaultOutputFile():
|
|
pref = preferences()
|
|
return pref.GetString(PostProcessorOutputFile, "")
|
|
|
|
|
|
def defaultOutputPolicy():
|
|
pref = preferences()
|
|
return pref.GetString(PostProcessorOutputPolicy, "")
|
|
|
|
|
|
def defaultStockTemplate():
|
|
return preferences().GetString(DefaultStockTemplate, "")
|
|
|
|
|
|
def setDefaultStockTemplate(template):
|
|
preferences().SetString(DefaultStockTemplate, template)
|
|
|
|
|
|
def defaultTaskPanelLayout():
|
|
return preferences().GetInt(DefaultTaskPanelLayout, 0)
|
|
|
|
|
|
def setDefaultTaskPanelLayout(style):
|
|
preferences().SetInt(DefaultTaskPanelLayout, style)
|
|
|
|
|
|
def advancedOCLFeaturesEnabled():
|
|
return preferences().GetBool(EnableAdvancedOCLFeatures, False)
|
|
|
|
|
|
def experimentalFeaturesEnabled():
|
|
return preferences().GetBool(EnableExperimentalFeatures, False)
|
|
|
|
|
|
def suppressAllSpeedsWarning():
|
|
return preferences().GetBool(WarningSuppressAllSpeeds, True)
|
|
|
|
|
|
def suppressRapidSpeedsWarning(user=True):
|
|
return (user and suppressAllSpeedsWarning()) or preferences().GetBool(
|
|
WarningSuppressRapidSpeeds, True
|
|
)
|
|
|
|
|
|
def suppressSelectionModeWarning():
|
|
return preferences().GetBool(WarningSuppressSelectionMode, True)
|
|
|
|
|
|
def suppressOpenCamLibWarning():
|
|
return preferences().GetBool(WarningSuppressOpenCamLib, True)
|
|
|
|
|
|
def suppressVelocity():
|
|
return preferences().GetBool(WarningSuppressVelocity, False)
|
|
|
|
|
|
def setPreferencesAdvanced(ocl, warnSpeeds, warnRapids, warnModes, warnOCL, warnVelocity):
|
|
preferences().SetBool(EnableAdvancedOCLFeatures, ocl)
|
|
preferences().SetBool(WarningSuppressAllSpeeds, warnSpeeds)
|
|
preferences().SetBool(WarningSuppressRapidSpeeds, warnRapids)
|
|
preferences().SetBool(WarningSuppressSelectionMode, warnModes)
|
|
preferences().SetBool(WarningSuppressOpenCamLib, warnOCL)
|
|
preferences().SetBool(WarningSuppressVelocity, warnVelocity)
|