From 3a23ea25bb1cdc6dc4b66d8a7264289af57f719f Mon Sep 17 00:00:00 2001 From: Markus Lampert Date: Thu, 17 Oct 2019 22:25:49 -0700 Subject: [PATCH] Added search path and preferences support for tools --- src/Mod/Path/CMakeLists.txt | 35 +++++++++++ src/Mod/Path/PathScripts/PathPostProcessor.py | 2 +- src/Mod/Path/PathScripts/PathPreferences.py | 60 +++++++++++++++++-- src/Mod/Path/PathScripts/PathToolBit.py | 34 +++++++++-- src/Mod/Path/PathTests/TestPathPreferences.py | 60 +++++++++++++++++++ src/Mod/Path/PathTests/TestPathToolBit.py | 45 ++++++++++++++ src/Mod/Path/TestPathApp.py | 4 ++ 7 files changed, 230 insertions(+), 10 deletions(-) create mode 100644 src/Mod/Path/PathTests/TestPathPreferences.py create mode 100644 src/Mod/Path/PathTests/TestPathToolBit.py diff --git a/src/Mod/Path/CMakeLists.txt b/src/Mod/Path/CMakeLists.txt index cb1cc64b79..f700376e8c 100644 --- a/src/Mod/Path/CMakeLists.txt +++ b/src/Mod/Path/CMakeLists.txt @@ -136,6 +136,17 @@ SET(PathScripts_post_SRCS PathScripts/post/smoothie_post.py ) +SET(Tools_Bit_SRCS +) + +SET(Tools_Library_SRCS +) + +SET(Tools_Template_SRCS + Tools/Template/drill-straight.fcstd + Tools/Template/endmill-straight.fcstd + Tools/Template/v-bit.fcstd +) SET(PathTests_SRCS PathTests/__init__.py @@ -181,6 +192,9 @@ SET(Path_Images SET(all_files ${PathScripts_SRCS} ${PathScripts_post_SRCS} + ${Tools_Bit_SRCS} + ${Tools_Library_SRCS} + ${Tools_Template_SRCS} ${Path_Images} ) @@ -221,6 +235,27 @@ INSTALL( Mod/Path/PathScripts/post ) +INSTALL( + FILES + ${Tools_Bit_SRCS} + DESTINATION + Mod/Path/Tools/Bit +) + +INSTALL( + FILES + ${Tools_Library_SRCS} + DESTINATION + Mod/Path/Tools/Library +) + +INSTALL( + FILES + ${Tools_Template_SRCS} + DESTINATION + Mod/Path/Tools/Template +) + INSTALL( FILES ${PathImages_Ops} diff --git a/src/Mod/Path/PathScripts/PathPostProcessor.py b/src/Mod/Path/PathScripts/PathPostProcessor.py index 82143bf998..605786f845 100644 --- a/src/Mod/Path/PathScripts/PathPostProcessor.py +++ b/src/Mod/Path/PathScripts/PathPostProcessor.py @@ -38,7 +38,7 @@ class PostProcessor: def load(cls, processor): PathLog.track(processor) syspath = sys.path - paths = PathPreferences.searchPaths() + paths = PathPreferences.searchPathsPost() paths.extend(sys.path) sys.path = paths diff --git a/src/Mod/Path/PathScripts/PathPreferences.py b/src/Mod/Path/PathScripts/PathPreferences.py index b2c0de8d43..9aa1c13f7d 100644 --- a/src/Mod/Path/PathScripts/PathPreferences.py +++ b/src/Mod/Path/PathScripts/PathPreferences.py @@ -41,6 +41,10 @@ PostProcessorBlacklist = "PostProcessorBlacklist" PostProcessorOutputFile = "PostProcessorOutputFile" PostProcessorOutputPolicy = "PostProcessorOutputPolicy" +LastPathToolBit = "LastPathToolBit" +LastPathToolLibrary = "LastPathToolLibrary" +LastPathToolTemplate = "LastPathToolTemplate" + # Linear tolerance to use when generating Paths, eg when tessellating geometry GeometryTolerance = "GeometryTolerance" LibAreaCurveAccuracy = "LibAreaCurveAccuarcy" @@ -52,14 +56,16 @@ def preferences(): return FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Path") def pathScriptsSourcePath(): - return FreeCAD.getHomePath() + ("Mod/Path/PathScripts/") + return os.path.join(FreeCAD.getHomePath(), "Mod/Path/PathScripts/") -def pathScriptsPostSourcePath(): - return pathScriptsSourcePath() + ("/post/") +def pathDefaultToolsPath(sub=None): + if sub: + return os.path.join(FreeCAD.getHomePath(), "Mod/Path/Tools/", sub) + return os.path.join(FreeCAD.getHomePath(), "Mod/Path/Tools/") def allAvailablePostProcessors(): allposts = [] - for path in searchPaths(): + 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() @@ -108,10 +114,38 @@ def searchPaths(): if p: paths.append(p) paths.append(macroFilePath()) - paths.append(pathScriptsPostSourcePath()) + return paths + +def searchPathsPost(): + paths = [] + p = defaultFilePath() + if p: + paths.append(p) + paths.append(macroFilePath()) + paths.append(os.path.join(pathScriptsSourcePath(), "post/")) paths.append(pathScriptsSourcePath()) return paths +def searchPathsTool(sub='Bit'): + paths = [] + + if 'Bit' == sub: + paths.append(lastPathToolBit()) + if 'Library' == sub: + paths.append(lastPathToolLibrary()) + if 'Template' == sub: + paths.append(lastPathToolTemplate()) + + def appendPath(p, sub): + if p: + paths.append(os.path.join(p, 'Tools', sub)) + paths.append(os.path.join(p, sub)) + paths.append(p) + appendPath(defaultFilePath(), sub) + appendPath(macroFilePath(), sub) + appendPath(os.path.join(FreeCAD.getHomePath(), "Mod/Path/"), sub) + return paths + def defaultJobTemplate(): template = preferences().GetString(DefaultJobTemplate) if 'xml' not in template: @@ -165,3 +199,19 @@ def setDefaultTaskPanelLayout(style): def experimentalFeaturesEnabled(): return preferences().GetBool(EnableExperimentalFeatures, False) + +def lastPathToolBit(): + return preferences().GetString(LastPathToolBit, pathDefaultToolsPath('Bit')) +def setLastPathToolBit(path): + return preferences().SetString(LastPathToolBit, path) + +def lastPathToolLibrary(): + return preferences().GetString(LastPathToolLibrary, pathDefaultToolsPath('Library')) +def setLastPathToolLibrary(path): + return preferences().SetString(LastPathToolLibrary, path) + +def lastPathToolTemplate(): + return preferences().GetString(LastPathToolTemplate, pathDefaultToolsPath('Template')) +def setLastPathToolTemplate(path): + return preferences().SetString(LastPathToolTemplate, path) + diff --git a/src/Mod/Path/PathScripts/PathToolBit.py b/src/Mod/Path/PathScripts/PathToolBit.py index b3956e8e8a..25825a1e67 100644 --- a/src/Mod/Path/PathScripts/PathToolBit.py +++ b/src/Mod/Path/PathScripts/PathToolBit.py @@ -26,12 +26,14 @@ import FreeCAD import Part import PathScripts.PathGeom as PathGeom import PathScripts.PathLog as PathLog +import PathScripts.PathPreferences as PathPreferences import PathScripts.PathSetupSheetOpPrototype as PathSetupSheetOpPrototype import PathScripts.PathUtil as PathUtil import PySide import Sketcher import json import math +import os import zipfile __title__ = "Tool bits." @@ -54,6 +56,28 @@ ParameterTypeConstraint = { } +def _findTool(path, typ): + if os.path.exists(path): + return path + + def searchFor(pname, fname): + if fname: + for p in PathPreferences.searchPathsTool(typ): + f = os.path.join(p, fname) + if os.path.exists(f): + return f + if pname and '/' != pname: + ppname, pfname = os.path.split(pname) + ffname = os.path.join(pfname, fname) if fname else pfname + return searchFor(ppname, ffname) + return None + + return searchFor(path, '') + +def findTemplate(path): + '''findTemplate(path) ... search for path, full and partially in all known template directories.''' + return _findTool(path, 'Template') + def updateConstraint(sketch, name, value): for i, constraint in enumerate(sketch.Constraints): if constraint.Name.split(';')[0] == name: @@ -136,16 +160,18 @@ class ToolBit(object): obj.Shape = Part.Shape() def _loadBitBody(self, obj, path=None): - if not path: - path = obj.BitTemplate + p = path if path else obj.BitTemplate docOpened = False doc = None for d in FreeCAD.listDocuments(): - if FreeCAD.getDocument(d).FileName == path: + if FreeCAD.getDocument(d).FileName == p: doc = FreeCAD.getDocument(d) break if doc is None: - doc = FreeCAD.open(path) + p = findTemplate(p) + if not path and p != obj.BitTemplate: + obj.BitTemplate = p + doc = FreeCAD.open(p) docOpened = True return (doc, docOpened) diff --git a/src/Mod/Path/PathTests/TestPathPreferences.py b/src/Mod/Path/PathTests/TestPathPreferences.py new file mode 100644 index 0000000000..3696473125 --- /dev/null +++ b/src/Mod/Path/PathTests/TestPathPreferences.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- + +# *************************************************************************** +# * * +# * Copyright (c) 2019 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 PathScripts.PathPreferences as PathPreferences +import PathTests.PathTestUtils as PathTestUtils + +class TestPathPreferences(PathTestUtils.PathTestBase): + + def test00(self): + '''There is at least one search path.''' + + paths = PathPreferences.searchPaths() + self.assertGreater(len(paths), 0) + + def test01(self): + '''PathScripts is part of the posts search path.''' + paths = PathPreferences.searchPathsPost() + self.assertEqual(len([p for p in paths if p.endswith('/PathScripts/')]), 1) + + def test02(self): + '''PathScripts/post is part of the posts search path.''' + paths = PathPreferences.searchPathsPost() + self.assertEqual(len([p for p in paths if p.endswith('/PathScripts/post/')]), 1) + + def test03(self): + '''Available post processors include linuxcnc, grbl and opensbp.''' + posts = PathPreferences.allAvailablePostProcessors() + self.assertTrue('linuxcnc' in posts) + self.assertTrue('grbl' in posts) + self.assertTrue('opensbp' in posts) + + + def test10(self): + '''Default paths for tools are resolved correctly''' + + self.assertTrue(PathPreferences.pathDefaultToolsPath().endswith('/Path/Tools/')) + self.assertTrue(PathPreferences.pathDefaultToolsPath('Bit').endswith('/Path/Tools/Bit')) + self.assertTrue(PathPreferences.pathDefaultToolsPath('Library').endswith('/Path/Tools/Library')) + self.assertTrue(PathPreferences.pathDefaultToolsPath('Template').endswith('/Path/Tools/Template')) diff --git a/src/Mod/Path/PathTests/TestPathToolBit.py b/src/Mod/Path/PathTests/TestPathToolBit.py new file mode 100644 index 0000000000..1b1f2ba714 --- /dev/null +++ b/src/Mod/Path/PathTests/TestPathToolBit.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- + +# *************************************************************************** +# * * +# * Copyright (c) 2019 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 PathScripts.PathToolBit as PathToolBit +import PathTests.PathTestUtils as PathTestUtils + + +class TestPathToolBit(PathTestUtils.PathTestBase): + + def test00(self): + '''Find a tool template from file name''' + + path = PathToolBit.findTemplate('endmill-straight.fcstd') + self.assertIsNot(path, None) + self.assertNotEqual(path, 'endmill-straight.fcstd') + + def test01(self): + '''Find a tool template from an invalid absolute path.''' + + path = PathToolBit.findTemplate('/this/is/unlikely/a/valid/path/v-bit.fcstd') + self.assertIsNot(path, None) + self.assertNotEqual(path, '/this/is/unlikely/a/valid/path/v-bit.fcstd') + + diff --git a/src/Mod/Path/TestPathApp.py b/src/Mod/Path/TestPathApp.py index 55fbc78fa1..b5ac4e215c 100644 --- a/src/Mod/Path/TestPathApp.py +++ b/src/Mod/Path/TestPathApp.py @@ -25,6 +25,7 @@ import TestApp from PathTests.TestPathLog import TestPathLog +from PathTests.TestPathPreferences import TestPathPreferences from PathTests.TestPathCore import TestPathCore #from PathTests.TestPathPost import PathPostTestCases from PathTests.TestPathGeom import TestPathGeom @@ -35,6 +36,7 @@ from PathTests.TestPathDressupHoldingTags import TestHoldingTags from PathTests.TestPathDressupDogbone import TestDressupDogbone from PathTests.TestPathStock import TestPathStock from PathTests.TestPathTool import TestPathTool +from PathTests.TestPathToolBit import TestPathToolBit from PathTests.TestPathTooltable import TestPathTooltable from PathTests.TestPathToolController import TestPathToolController from PathTests.TestPathSetupSheet import TestPathSetupSheet @@ -58,4 +60,6 @@ False if TestPathToolController.__name__ else True False if TestPathSetupSheet.__name__ else True False if TestPathDeburr.__name__ else True False if TestPathHelix.__name__ else True +False if TestPathPreferences.__name__ else True +False if TestPathToolBit.__name__ else True