Merge pull request #2724 from mlampert/feature/tool-bit-poc
Path: Feature/tool bit poc
This commit is contained in:
@@ -101,6 +101,12 @@ SET(PathScripts_SRCS
|
||||
PathScripts/PathStop.py
|
||||
PathScripts/PathSurface.py
|
||||
PathScripts/PathSurfaceGui.py
|
||||
PathScripts/PathToolBit.py
|
||||
PathScripts/PathToolBitCmd.py
|
||||
PathScripts/PathToolBitEdit.py
|
||||
PathScripts/PathToolBitGui.py
|
||||
PathScripts/PathToolBitLibraryCmd.py
|
||||
PathScripts/PathToolBitLibraryGui.py
|
||||
PathScripts/PathToolController.py
|
||||
PathScripts/PathToolControllerGui.py
|
||||
PathScripts/PathToolEdit.py
|
||||
@@ -133,6 +139,29 @@ SET(PathScripts_post_SRCS
|
||||
PathScripts/post/smoothie_post.py
|
||||
)
|
||||
|
||||
SET(Tools_Bit_SRCS
|
||||
Tools/Bit/t1.fctb
|
||||
Tools/Bit/t2.fctb
|
||||
Tools/Bit/t3.fctb
|
||||
Tools/Bit/t4.fctb
|
||||
Tools/Bit/t5.fctb
|
||||
Tools/Bit/t6.fctb
|
||||
Tools/Bit/t7.fctb
|
||||
Tools/Bit/t8.fctb
|
||||
Tools/Bit/t9.fctb
|
||||
)
|
||||
|
||||
SET(Tools_Library_SRCS
|
||||
Tools/Library/endmills.fctl
|
||||
)
|
||||
|
||||
SET(Tools_Shape_SRCS
|
||||
Tools/Shape/ballend.fcstd
|
||||
Tools/Shape/bullnose.fcstd
|
||||
Tools/Shape/drill.fcstd
|
||||
Tools/Shape/endmill.fcstd
|
||||
Tools/Shape/v-bit.fcstd
|
||||
)
|
||||
|
||||
SET(PathTests_SRCS
|
||||
PathTests/__init__.py
|
||||
@@ -147,9 +176,11 @@ SET(PathTests_SRCS
|
||||
PathTests/TestPathLog.py
|
||||
PathTests/TestPathOpTools.py
|
||||
PathTests/TestPathPost.py
|
||||
PathTests/TestPathPreferences.py
|
||||
PathTests/TestPathSetupSheet.py
|
||||
PathTests/TestPathStock.py
|
||||
PathTests/TestPathTool.py
|
||||
PathTests/TestPathToolBit.py
|
||||
PathTests/TestPathToolController.py
|
||||
PathTests/TestPathTooltable.py
|
||||
PathTests/TestPathUtil.py
|
||||
@@ -178,6 +209,9 @@ SET(Path_Images
|
||||
SET(all_files
|
||||
${PathScripts_SRCS}
|
||||
${PathScripts_post_SRCS}
|
||||
${Tools_Bit_SRCS}
|
||||
${Tools_Library_SRCS}
|
||||
${Tools_Shape_SRCS}
|
||||
${Path_Images}
|
||||
)
|
||||
|
||||
@@ -218,6 +252,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_Shape_SRCS}
|
||||
DESTINATION
|
||||
Mod/Path/Tools/Shape
|
||||
)
|
||||
|
||||
INSTALL(
|
||||
FILES
|
||||
${PathImages_Ops}
|
||||
|
||||
@@ -48,9 +48,10 @@
|
||||
<file>icons/Path-Speed.svg</file>
|
||||
<file>icons/Path-Stock.svg</file>
|
||||
<file>icons/Path-Stop.svg</file>
|
||||
<file>icons/Path-ToolBit.svg</file>
|
||||
<file>icons/Path-ToolChange.svg</file>
|
||||
<file>icons/Path-ToolController.svg</file>
|
||||
<file>icons/Path-ToolDuplicate.svg</file>
|
||||
<file>icons/Path-ToolDuplicate.svg</file>
|
||||
<file>icons/Path-Toolpath.svg</file>
|
||||
<file>icons/Path-ToolTable.svg</file>
|
||||
<file>icons/Path-Area.svg</file>
|
||||
@@ -107,6 +108,9 @@
|
||||
<file>panels/PointEdit.ui</file>
|
||||
<file>panels/SetupGlobal.ui</file>
|
||||
<file>panels/SetupOp.ui</file>
|
||||
<file>panels/ToolBitEditor.ui</file>
|
||||
<file>panels/ToolBitLibraryEdit.ui</file>
|
||||
<file>panels/ToolBitSelector.ui</file>
|
||||
<file>panels/ToolEditor.ui</file>
|
||||
<file>panels/ToolLibraryEditor.ui</file>
|
||||
<file>panels/TaskPathSimulator.ui</file>
|
||||
|
||||
933
src/Mod/Path/Gui/Resources/icons/Path-ToolBit.svg
Normal file
933
src/Mod/Path/Gui/Resources/icons/Path-ToolBit.svg
Normal file
@@ -0,0 +1,933 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="64px"
|
||||
height="64px"
|
||||
id="svg2816"
|
||||
version="1.1"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
|
||||
sodipodi:docname="Path-Tool.svg">
|
||||
<defs
|
||||
id="defs2818">
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3898">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3900" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3902" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient4513">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4515" />
|
||||
<stop
|
||||
style="stop-color:#999999;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4517" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient3681">
|
||||
<stop
|
||||
id="stop3697"
|
||||
offset="0"
|
||||
style="stop-color:#fff110;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#cf7008;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3685" />
|
||||
</linearGradient>
|
||||
<inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 32 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_z="64 : 32 : 1"
|
||||
inkscape:persp3d-origin="32 : 21.333333 : 1"
|
||||
id="perspective2824" />
|
||||
<inkscape:perspective
|
||||
id="perspective3622"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3622-9"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3653"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3675"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3697"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3720"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3742"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3764"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3785"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3806"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3806-3"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3835"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3614"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3614-8"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3643"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3643-3"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3672"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3672-5"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3701"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3701-8"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective3746"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<pattern
|
||||
patternTransform="matrix(0.67643728,-0.81829155,2.4578314,1.8844554,-26.450606,18.294947)"
|
||||
id="pattern5231"
|
||||
xlink:href="#Strips1_1-4"
|
||||
inkscape:collect="always" />
|
||||
<inkscape:perspective
|
||||
id="perspective5224"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<pattern
|
||||
inkscape:stockid="Stripes 1:1"
|
||||
id="Strips1_1-4"
|
||||
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
|
||||
height="1"
|
||||
width="2"
|
||||
patternUnits="userSpaceOnUse"
|
||||
inkscape:collect="always">
|
||||
<rect
|
||||
id="rect4483-4"
|
||||
height="2"
|
||||
width="1"
|
||||
y="-0.5"
|
||||
x="0"
|
||||
style="fill:black;stroke:none" />
|
||||
</pattern>
|
||||
<inkscape:perspective
|
||||
id="perspective5224-9"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<pattern
|
||||
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,39.618381,8.9692804)"
|
||||
id="pattern5231-4"
|
||||
xlink:href="#Strips1_1-6"
|
||||
inkscape:collect="always" />
|
||||
<inkscape:perspective
|
||||
id="perspective5224-3"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<pattern
|
||||
inkscape:stockid="Stripes 1:1"
|
||||
id="Strips1_1-6"
|
||||
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
|
||||
height="1"
|
||||
width="2"
|
||||
patternUnits="userSpaceOnUse"
|
||||
inkscape:collect="always">
|
||||
<rect
|
||||
id="rect4483-0"
|
||||
height="2"
|
||||
width="1"
|
||||
y="-0.5"
|
||||
x="0"
|
||||
style="fill:black;stroke:none" />
|
||||
</pattern>
|
||||
<pattern
|
||||
patternTransform="matrix(0.66513382,-1.0631299,2.4167603,2.4482973,-49.762569,2.9546807)"
|
||||
id="pattern5296"
|
||||
xlink:href="#pattern5231-3"
|
||||
inkscape:collect="always" />
|
||||
<inkscape:perspective
|
||||
id="perspective5288"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<pattern
|
||||
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,-26.336284,10.887197)"
|
||||
id="pattern5231-3"
|
||||
xlink:href="#Strips1_1-4-3"
|
||||
inkscape:collect="always" />
|
||||
<pattern
|
||||
inkscape:stockid="Stripes 1:1"
|
||||
id="Strips1_1-4-3"
|
||||
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
|
||||
height="1"
|
||||
width="2"
|
||||
patternUnits="userSpaceOnUse"
|
||||
inkscape:collect="always">
|
||||
<rect
|
||||
id="rect4483-4-6"
|
||||
height="2"
|
||||
width="1"
|
||||
y="-0.5"
|
||||
x="0"
|
||||
style="fill:black;stroke:none" />
|
||||
</pattern>
|
||||
<pattern
|
||||
patternTransform="matrix(0.42844886,-0.62155849,1.5567667,1.431396,27.948414,13.306456)"
|
||||
id="pattern5330"
|
||||
xlink:href="#Strips1_1-9"
|
||||
inkscape:collect="always" />
|
||||
<inkscape:perspective
|
||||
id="perspective5323"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<pattern
|
||||
inkscape:stockid="Stripes 1:1"
|
||||
id="Strips1_1-9"
|
||||
patternTransform="matrix(0.66772843,-1.0037085,2.4261878,2.3114548,3.4760987,3.534923)"
|
||||
height="1"
|
||||
width="2"
|
||||
patternUnits="userSpaceOnUse"
|
||||
inkscape:collect="always">
|
||||
<rect
|
||||
id="rect4483-3"
|
||||
height="2"
|
||||
width="1"
|
||||
y="-0.5"
|
||||
x="0"
|
||||
style="fill:black;stroke:none" />
|
||||
</pattern>
|
||||
<inkscape:perspective
|
||||
id="perspective5361"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective5383"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<inkscape:perspective
|
||||
id="perspective5411"
|
||||
inkscape:persp3d-origin="0.5 : 0.33333333 : 1"
|
||||
inkscape:vp_z="1 : 0.5 : 1"
|
||||
inkscape:vp_y="0 : 1000 : 0"
|
||||
inkscape:vp_x="0 : 0.5 : 1"
|
||||
sodipodi:type="inkscape:persp3d" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3681"
|
||||
id="linearGradient3687"
|
||||
x1="37.89756"
|
||||
y1="41.087898"
|
||||
x2="4.0605712"
|
||||
y2="40.168594"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(127.27273,-51.272729)" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3681"
|
||||
id="linearGradient3695"
|
||||
x1="37.894287"
|
||||
y1="40.484772"
|
||||
x2="59.811455"
|
||||
y2="43.558987"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(127.27273,-51.272729)" />
|
||||
<linearGradient
|
||||
id="linearGradient3681-3">
|
||||
<stop
|
||||
id="stop3697-3"
|
||||
offset="0"
|
||||
style="stop-color:#fff110;stop-opacity:1;" />
|
||||
<stop
|
||||
style="stop-color:#cf7008;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop3685-4" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="43.558987"
|
||||
x2="59.811455"
|
||||
y1="40.484772"
|
||||
x1="37.894287"
|
||||
gradientTransform="translate(-37.00068,-20.487365)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3608"
|
||||
xlink:href="#linearGradient3681-3"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4513-2">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4515-2" />
|
||||
<stop
|
||||
style="stop-color:#999999;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4517-4" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="23.634638"
|
||||
fy="7.9319997"
|
||||
fx="32.151962"
|
||||
cy="7.9319997"
|
||||
cx="32.151962"
|
||||
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient4538"
|
||||
xlink:href="#linearGradient4513-2"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4513-1">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4515-8" />
|
||||
<stop
|
||||
style="stop-color:#999999;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4517-6" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="23.634638"
|
||||
fy="7.9319997"
|
||||
fx="32.151962"
|
||||
cy="7.9319997"
|
||||
cx="32.151962"
|
||||
gradientTransform="matrix(1,0,0,1.1841158,-8.5173246,-3.4097568)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient4538-6"
|
||||
xlink:href="#linearGradient4513-1"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4513-1-3">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4515-8-7" />
|
||||
<stop
|
||||
style="stop-color:#999999;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4517-6-5" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="23.634638"
|
||||
fy="35.869175"
|
||||
fx="32.151962"
|
||||
cy="35.869175"
|
||||
cx="32.151962"
|
||||
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3069"
|
||||
xlink:href="#linearGradient4513-1-3"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4513-1-2">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4515-8-6" />
|
||||
<stop
|
||||
style="stop-color:#999999;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4517-6-6" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="23.634638"
|
||||
fy="35.869175"
|
||||
fx="32.151962"
|
||||
cy="35.869175"
|
||||
cx="32.151962"
|
||||
gradientTransform="matrix(0.39497909,0,0,1.1841158,-2.716491,-26.067007)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3102"
|
||||
xlink:href="#linearGradient4513-1-2"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4513-1"
|
||||
id="radialGradient3132"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.39497909,0,0,1.1841158,41.43166,2.0473921)"
|
||||
cx="32.151962"
|
||||
cy="27.950663"
|
||||
fx="32.151962"
|
||||
fy="27.950663"
|
||||
r="23.634638" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4513-1-27"
|
||||
id="radialGradient3132-0"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.39497909,0,0,1.1841158,38.088973,2.175957)"
|
||||
cx="32.151962"
|
||||
cy="27.950663"
|
||||
fx="32.151962"
|
||||
fy="27.950663"
|
||||
r="23.634638" />
|
||||
<linearGradient
|
||||
id="linearGradient4513-1-27">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4515-8-1" />
|
||||
<stop
|
||||
style="stop-color:#999999;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4517-6-0" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="23.634638"
|
||||
fy="27.950663"
|
||||
fx="32.151962"
|
||||
cy="27.950663"
|
||||
cx="32.151962"
|
||||
gradientTransform="matrix(0.39497909,0,0,1.1841158,18.770923,-9.8531183)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3101"
|
||||
xlink:href="#linearGradient4513-1-27"
|
||||
inkscape:collect="always" />
|
||||
<radialGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4513-1-5"
|
||||
id="radialGradient3132-02"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="matrix(0.39497909,0,0,1.1841158,38.088973,2.175957)"
|
||||
cx="32.151962"
|
||||
cy="27.950663"
|
||||
fx="32.151962"
|
||||
fy="27.950663"
|
||||
r="23.634638" />
|
||||
<linearGradient
|
||||
id="linearGradient4513-1-5">
|
||||
<stop
|
||||
style="stop-color:#ffffff;stop-opacity:1;"
|
||||
offset="0"
|
||||
id="stop4515-8-10" />
|
||||
<stop
|
||||
style="stop-color:#999999;stop-opacity:1;"
|
||||
offset="1"
|
||||
id="stop4517-6-1" />
|
||||
</linearGradient>
|
||||
<radialGradient
|
||||
r="23.634638"
|
||||
fy="27.950663"
|
||||
fx="32.151962"
|
||||
cy="27.950663"
|
||||
cx="32.151962"
|
||||
gradientTransform="matrix(0.39497909,0,0,1.1841158,-1.4349938,4.2466976)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="radialGradient3101-6"
|
||||
xlink:href="#linearGradient4513-1-5"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(22,2)"
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3989"
|
||||
id="linearGradient3987"
|
||||
x1="31"
|
||||
y1="15"
|
||||
x2="33"
|
||||
y2="23"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient3989">
|
||||
<stop
|
||||
id="stop3991"
|
||||
offset="0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop3993"
|
||||
offset="1"
|
||||
style="stop-color:#888a85;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4031"
|
||||
id="linearGradient4029"
|
||||
x1="27.909092"
|
||||
y1="27.90909"
|
||||
x2="36"
|
||||
y2="54.227272"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
id="linearGradient4031">
|
||||
<stop
|
||||
id="stop4033"
|
||||
offset="0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop4035"
|
||||
offset="1"
|
||||
style="stop-color:#888a85;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(20,3)"
|
||||
y2="54"
|
||||
x2="35"
|
||||
y1="11"
|
||||
x1="29"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient4154"
|
||||
xlink:href="#linearGradient4031"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(20,3)"
|
||||
y2="54"
|
||||
x2="35"
|
||||
y1="11"
|
||||
x1="29"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient4154-7"
|
||||
xlink:href="#linearGradient4031-5"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4031-5">
|
||||
<stop
|
||||
id="stop4033-3"
|
||||
offset="0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop4035-5"
|
||||
offset="1"
|
||||
style="stop-color:#888a85;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="54"
|
||||
x2="35"
|
||||
y1="11"
|
||||
x1="29"
|
||||
gradientTransform="translate(-2,-8)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3118"
|
||||
xlink:href="#linearGradient4031-5"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(20,3)"
|
||||
y2="54"
|
||||
x2="35"
|
||||
y1="11"
|
||||
x1="29"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient4154-9"
|
||||
xlink:href="#linearGradient4031-1"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4031-1">
|
||||
<stop
|
||||
id="stop4033-2"
|
||||
offset="0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop4035-7"
|
||||
offset="1"
|
||||
style="stop-color:#888a85;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
y2="54"
|
||||
x2="35"
|
||||
y1="11"
|
||||
x1="29"
|
||||
gradientTransform="translate(-24,10)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient3118-0"
|
||||
xlink:href="#linearGradient4031-1"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
gradientTransform="translate(20,3)"
|
||||
y2="54"
|
||||
x2="35"
|
||||
y1="11"
|
||||
x1="29"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient4154-8"
|
||||
xlink:href="#linearGradient4031-7"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4031-7">
|
||||
<stop
|
||||
id="stop4033-9"
|
||||
offset="0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop4035-2"
|
||||
offset="1"
|
||||
style="stop-color:#888a85;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
gradientTransform="translate(20,3)"
|
||||
y2="54"
|
||||
x2="35"
|
||||
y1="11"
|
||||
x1="29"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id="linearGradient4154-5"
|
||||
xlink:href="#linearGradient4031-9"
|
||||
inkscape:collect="always" />
|
||||
<linearGradient
|
||||
id="linearGradient4031-9">
|
||||
<stop
|
||||
id="stop4033-22"
|
||||
offset="0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop4035-8"
|
||||
offset="1"
|
||||
style="stop-color:#888a85;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient4031"
|
||||
id="linearGradient4030"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
gradientTransform="translate(20,3)"
|
||||
x1="29"
|
||||
y1="11"
|
||||
x2="35"
|
||||
y2="54" />
|
||||
<linearGradient
|
||||
id="linearGradient4031-74">
|
||||
<stop
|
||||
id="stop4033-0"
|
||||
offset="0"
|
||||
style="stop-color:#d3d7cf;stop-opacity:1" />
|
||||
<stop
|
||||
id="stop4035-9"
|
||||
offset="1"
|
||||
style="stop-color:#888a85;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3898"
|
||||
id="linearGradient3904"
|
||||
x1="35.05999"
|
||||
y1="53.008698"
|
||||
x2="27.286415"
|
||||
y2="7.311924"
|
||||
gradientUnits="userSpaceOnUse" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3898-1"
|
||||
id="linearGradient3920-6"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="35.05999"
|
||||
y1="53.008698"
|
||||
x2="27.286415"
|
||||
y2="7.311924" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3898-1">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3900-4" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3902-2" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3898-1"
|
||||
id="linearGradient3916-3"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="35.05999"
|
||||
y1="53.008698"
|
||||
x2="27.286415"
|
||||
y2="7.311924" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3898-1"
|
||||
id="linearGradient3918-2"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="35.05999"
|
||||
y1="53.008698"
|
||||
x2="27.286415"
|
||||
y2="7.311924" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3898-8-9"
|
||||
id="linearGradient3920-8-8"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="35.05999"
|
||||
y1="53.008698"
|
||||
x2="27.286415"
|
||||
y2="7.311924" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
id="linearGradient3898-8-9">
|
||||
<stop
|
||||
style="stop-color:#888a85;stop-opacity:1"
|
||||
offset="0"
|
||||
id="stop3900-2-2" />
|
||||
<stop
|
||||
style="stop-color:#d3d7cf;stop-opacity:1"
|
||||
offset="1"
|
||||
id="stop3902-4-7" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3898-8-9"
|
||||
id="linearGradient3916-5-9"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="35.05999"
|
||||
y1="53.008698"
|
||||
x2="27.286415"
|
||||
y2="7.311924" />
|
||||
<linearGradient
|
||||
inkscape:collect="always"
|
||||
xlink:href="#linearGradient3898-8-9"
|
||||
id="linearGradient3918-5-5"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
x1="35.05999"
|
||||
y1="53.008698"
|
||||
x2="27.286415"
|
||||
y2="7.311924" />
|
||||
</defs>
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="8.0000004"
|
||||
inkscape:cx="-26.037067"
|
||||
inkscape:cy="38.278529"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
inkscape:grid-bbox="true"
|
||||
inkscape:snap-bbox="true"
|
||||
inkscape:bbox-paths="false"
|
||||
inkscape:bbox-nodes="false"
|
||||
inkscape:snap-bbox-edge-midpoints="false"
|
||||
inkscape:snap-bbox-midpoints="false"
|
||||
inkscape:object-paths="true"
|
||||
inkscape:object-nodes="true"
|
||||
inkscape:window-width="3064"
|
||||
inkscape:window-height="1598"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="34"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:snap-global="true"
|
||||
inkscape:snap-nodes="false">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid3067"
|
||||
empspacing="2"
|
||||
visible="true"
|
||||
enabled="true"
|
||||
snapvisiblegridlinesonly="true" />
|
||||
</sodipodi:namedview>
|
||||
<metadata
|
||||
id="metadata2821">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
<dc:title>Path-ToolTable</dc:title>
|
||||
<dc:date>2015-07-04</dc:date>
|
||||
<dc:relation>http://www.freecadweb.org/wiki/index.php?title=Artwork</dc:relation>
|
||||
<dc:publisher>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:identifier>FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-ToolTable.svg</dc:identifier>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>FreeCAD LGPL2+</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<cc:license>https://www.gnu.org/copyleft/lesser.html</cc:license>
|
||||
<dc:contributor>
|
||||
<cc:Agent>
|
||||
<dc:title>[agryson] Alexander Gryson</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:contributor>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1"
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer">
|
||||
<g
|
||||
id="g3908-2"
|
||||
transform="translate(-41.849711,-4)">
|
||||
<g
|
||||
id="g3859-1"
|
||||
transform="matrix(0.78612717,0,0,1,48.768786,3)">
|
||||
<g
|
||||
style="fill:url(#linearGradient3920-6);fill-opacity:1"
|
||||
id="g3126-6">
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccccccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="rect4417-8"
|
||||
d="m 23,28 0,9 18,-8 0,-9 z m 18,3 -18,8 0,9 18,-8 z m 0,11 -18,8 9,6 9,-6 z"
|
||||
style="color:#000000;fill:url(#linearGradient3916-3);fill-opacity:1;fill-rule:nonzero;stroke:#2e3436;stroke-width:2.25571179;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3085-57"
|
||||
d="m 23.191177,26 0,-20 L 41,6 41,18 z"
|
||||
style="fill:url(#linearGradient3918-2);fill-opacity:1;stroke:#2e3436;stroke-width:2.25571179;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
|
||||
</g>
|
||||
<path
|
||||
style="color:#000000;fill:none;stroke:#d3d7cf;stroke-width:2.25571179;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
|
||||
d="m 25.536535,29.21875 0.02839,4.267045 L 38.41613,27.875 38.45588,23.40625 z m 12.925366,5.17498 -13.004096,5.795099 0.04815,4.413423 13.036237,-5.775011 z m -0.06224,11.048212 -10.969048,4.881281 4.537301,3.001812 L 38.455882,49 z"
|
||||
id="rect4417-1-4-6"
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccccccccc" />
|
||||
</g>
|
||||
<path
|
||||
sodipodi:nodetypes="ccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3906-1"
|
||||
d="M 69,11 69,25.53033 79.044194,19.779029 79,11 z"
|
||||
style="fill:none;stroke:#d3d7cf;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 29 KiB |
233
src/Mod/Path/Gui/Resources/panels/ToolBitEditor.ui
Normal file
233
src/Mod/Path/Gui/Resources/panels/ToolBitEditor.ui
Normal file
@@ -0,0 +1,233 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Form</class>
|
||||
<widget class="QWidget" name="Form">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>587</width>
|
||||
<height>744</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QToolBox" name="toolBox">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="page">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>559</width>
|
||||
<height>626</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Shape</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>Tool Bit</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QLineEdit" name="toolName">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Display name of the Tool Bit (initial value taken from the shape file).</p></body></html></string>
|
||||
</property>
|
||||
<property name="maxLength">
|
||||
<number>50</number>
|
||||
</property>
|
||||
<property name="placeholderText">
|
||||
<string>Display Name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Type</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="shapePath">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>The file which defines the type and shape of the Tool Bit.</p></body></html></string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="shapeSet">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Change file defining type and shape of Tool Bit.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="bitParams">
|
||||
<property name="title">
|
||||
<string>Bit Parameter</string>
|
||||
</property>
|
||||
<layout class="QFormLayout" name="formLayout_2">
|
||||
<property name="fieldGrowthPolicy">
|
||||
<enum>QFormLayout::AllNonFixedFieldsGrow</enum>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Point/Tip Angle</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="Gui::InputField" name="toolCuttingEdgeAngle">
|
||||
<property name="text">
|
||||
<string>180°</string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string>°</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Cutting Edge Height</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="Gui::InputField" name="toolCuttingEdgeHeight">
|
||||
<property name="text">
|
||||
<string>0.00</string>
|
||||
</property>
|
||||
<property name="unit" stdset="0">
|
||||
<string>mm</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="image">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>210</width>
|
||||
<height>297</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Image</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>277</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="page_2">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>559</width>
|
||||
<height>626</height>
|
||||
</rect>
|
||||
</property>
|
||||
<attribute name="label">
|
||||
<string>Attributes</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QTableView" name="attrTable">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="MinimumExpanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>2</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>300</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::AllEditTriggers</set>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::InputField</class>
|
||||
<extends>QLineEdit</extends>
|
||||
<header>Gui/InputField.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
276
src/Mod/Path/Gui/Resources/panels/ToolBitLibraryEdit.ui
Normal file
276
src/Mod/Path/Gui/Resources/panels/ToolBitLibraryEdit.ui
Normal file
@@ -0,0 +1,276 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>958</width>
|
||||
<height>508</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>ToolBit Library</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QToolButton" name="libraryNew">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Create a new library with an empty list of Tool Bits.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../../Gui/Icons/resource.qrc">
|
||||
<normaloff>:/icons/document-new.svg</normaloff>:/icons/document-new.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="libraryOpen">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Open an existing Tool Bit library.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../../Gui/Icons/resource.qrc">
|
||||
<normaloff>:/icons/document-open.svg</normaloff>:/icons/document-open.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="librarySave">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Save Tool Bit library.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../../Gui/Icons/resource.qrc">
|
||||
<normaloff>:/icons/document-save.svg</normaloff>:/icons/document-save.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="librarySaveAs">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Save Tool Bit library under new name.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../../Gui/Icons/resource.qrc">
|
||||
<normaloff>:/icons/document-save-as.svg</normaloff>:/icons/document-save-as.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="libraryPref">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Edit Tool Bit library editor settings.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../../Gui/Icons/resource.qrc">
|
||||
<normaloff>:/icons/preferences-system.svg</normaloff>:/icons/preferences-system.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="toolTableGroup" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableView" name="toolTable">
|
||||
<property name="acceptDrops">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Table of Tool Bits of the library.</p></body></html></string>
|
||||
</property>
|
||||
<property name="dragEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="dragDropMode">
|
||||
<enum>QAbstractItemView::InternalMove</enum>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::MoveAction</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="sortingEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget_3" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="toolAdd">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Add another Tool Bit to this library.</p><p><br/></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Add ...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../../Gui/Icons/resource.qrc">
|
||||
<normaloff>:/icons/list-add.svg</normaloff>:/icons/list-add.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="toolDelete">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Delete selected Tool Bit(s) from the library.</p><p><br/></p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../../Gui/Icons/resource.qrc">
|
||||
<normaloff>:/icons/list-remove.svg</normaloff>:/icons/list-remove.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="toolEnumerate">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Assigne numbers to each Tool Bit according to its current position in the library. The first Tool Bit is assigned the ID 1.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Enumerate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>115</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../../../../Gui/Icons/resource.qrc"/>
|
||||
</resources>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
119
src/Mod/Path/Gui/Resources/panels/ToolBitSelector.ui
Normal file
119
src/Mod/Path/Gui/Resources/panels/ToolBitSelector.ui
Normal file
@@ -0,0 +1,119 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Dialog</class>
|
||||
<widget class="QDialog" name="Dialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>588</width>
|
||||
<height>396</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QWidget" name="widget_2" native="true">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QListWidget" name="tools">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Available Tool Bits to choose from.</p></body></html></string>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="widget" native="true">
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QPushButton" name="toolLoad">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Load an existing Tool Bit from a file.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Load...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="toolCreate">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>Create a new Tool Bit based on an existing shape.</p></body></html></string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>Dialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
||||
File diff suppressed because one or more lines are too long
@@ -71,6 +71,16 @@ class PathWorkbench (Workbench):
|
||||
FreeCADGui.addIconPath(":/icons")
|
||||
from PathScripts import PathGuiInit
|
||||
from PathScripts import PathJobCmd
|
||||
|
||||
from PathScripts import PathToolBitCmd
|
||||
from PathScripts import PathToolBitLibraryCmd
|
||||
if PathPreferences.experimentalFeaturesEnabled():
|
||||
toolbitcmdlist = PathToolBitCmd.CommandList + ["Separator"] + PathToolBitLibraryCmd.CommandList + ["Path_ToolController", "Separator"]
|
||||
self.toolbitctxmenu = ["Path_ToolBitLibraryLoad", "Path_ToolController"]
|
||||
else:
|
||||
toolbitcmdlist = []
|
||||
self.toolbitctxmenu = []
|
||||
|
||||
import PathCommands
|
||||
PathGuiInit.Startup()
|
||||
|
||||
@@ -112,7 +122,7 @@ class PathWorkbench (Workbench):
|
||||
if extracmdlist:
|
||||
self.appendToolbar(QtCore.QT_TRANSLATE_NOOP("Path", "Helpful Tools"), extracmdlist)
|
||||
|
||||
self.appendMenu([QtCore.QT_TRANSLATE_NOOP("Path", "&Path")], projcmdlist +["Path_ExportTemplate", "Separator"] + toolcmdlist +["Separator"] + twodopcmdlist + engravecmdlist +["Separator"] +threedopcmdlist +["Separator"])
|
||||
self.appendMenu([QtCore.QT_TRANSLATE_NOOP("Path", "&Path")], projcmdlist +["Path_ExportTemplate", "Separator"] + toolbitcmdlist + toolcmdlist +["Separator"] + twodopcmdlist + engravecmdlist +["Separator"] +threedopcmdlist +["Separator"])
|
||||
self.appendMenu([QtCore.QT_TRANSLATE_NOOP("Path", "&Path"), QtCore.QT_TRANSLATE_NOOP(
|
||||
"Path", "Path Dressup")], dressupcmdlist)
|
||||
self.appendMenu([QtCore.QT_TRANSLATE_NOOP("Path", "&Path"), QtCore.QT_TRANSLATE_NOOP(
|
||||
@@ -143,6 +153,7 @@ class PathWorkbench (Workbench):
|
||||
|
||||
def ContextMenu(self, recipient):
|
||||
import PathScripts
|
||||
menuAppended = False
|
||||
if len(FreeCADGui.Selection.getSelection()) == 1:
|
||||
obj = FreeCADGui.Selection.getSelection()[0]
|
||||
if obj.isDerivedFrom("Path::Feature"):
|
||||
@@ -152,9 +163,11 @@ class PathWorkbench (Workbench):
|
||||
if "Remote" in selectedName:
|
||||
self.appendContextMenu("", ["Refresh_Path"])
|
||||
if "Job" in selectedName:
|
||||
self.appendContextMenu("", ["Path_ExportTemplate"])
|
||||
if isinstance (obj.Proxy, PathScripts.PathOp.ObjectOp):
|
||||
self.appendContextMenu("", ["Path_ExportTemplate"] + self.toolbitctxmenu)
|
||||
menuAppended = True
|
||||
if isinstance(obj.Proxy, PathScripts.PathOp.ObjectOp):
|
||||
self.appendContextMenu("", ["Path_OperationCopy", "Path_OpActiveToggle"])
|
||||
menuAppended = True
|
||||
if obj.isDerivedFrom("Path::Feature"):
|
||||
if "Profile" in selectedName or "Contour" in selectedName or "Dressup" in selectedName:
|
||||
self.appendContextMenu("", "Separator")
|
||||
@@ -162,6 +175,12 @@ class PathWorkbench (Workbench):
|
||||
#self.appendContextMenu("", ["Set_EndPoint"])
|
||||
for cmd in self.dressupcmds:
|
||||
self.appendContextMenu("", [cmd])
|
||||
menuAppended = True
|
||||
if isinstance(obj.Proxy, PathScripts.PathToolBit.ToolBit):
|
||||
self.appendContextMenu("", ["Path_ToolBitSave", "Path_ToolBitSaveAs"])
|
||||
menuAppended = True
|
||||
if menuAppended:
|
||||
self.appendContextMenu("", "Separator")
|
||||
|
||||
Gui.addWorkbench(PathWorkbench())
|
||||
|
||||
|
||||
@@ -143,7 +143,7 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
return shape.Curve.Radius * 2
|
||||
|
||||
if shape.ShapeType == 'Face':
|
||||
for i in range(len(shape.Edges)):
|
||||
for i in range(len(shape.Edges)):
|
||||
if (type(shape.Edges[i].Curve) == Part.Circle and
|
||||
shape.Edges[i].Curve.Radius * 2 < shape.BoundBox.XLength*1.1 and
|
||||
shape.Edges[i].Curve.Radius * 2 > shape.BoundBox.XLength*0.9):
|
||||
@@ -384,7 +384,7 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
if 1 == len(self.model) and self.baseIsArchPanel(obj, self.model[0]):
|
||||
panel = self.model[0]
|
||||
holeshapes = panel.Proxy.getHoles(panel, transform=True)
|
||||
tooldiameter = obj.ToolController.Proxy.getTool(obj.ToolController).Diameter
|
||||
tooldiameter = float(obj.ToolController.Proxy.getTool(obj.ToolController).Diameter)
|
||||
for holeNr, hole in enumerate(holeshapes):
|
||||
PathLog.debug('Entering new HoleShape')
|
||||
for wireNr, wire in enumerate(hole.Wires):
|
||||
@@ -405,7 +405,7 @@ class ObjectOp(PathOp.ObjectOp):
|
||||
PathLog.track('obj: {} shape: {}'.format(obj, shape))
|
||||
holelist = []
|
||||
features = []
|
||||
# tooldiameter = obj.ToolController.Proxy.getTool(obj.ToolController).Diameter
|
||||
# tooldiameter = float(obj.ToolController.Proxy.getTool(obj.ToolController).Diameter)
|
||||
tooldiameter = None
|
||||
PathLog.debug('search for holes larger than tooldiameter: {}: '.format(tooldiameter))
|
||||
if DraftGeomUtils.isPlanar(shape):
|
||||
|
||||
@@ -48,15 +48,15 @@ def translate(context, text, disambig=None):
|
||||
|
||||
def toolDepthAndOffset(width, extraDepth, tool):
|
||||
'''toolDepthAndOffset(width, extraDepth, tool) ... return tuple for given parameters.'''
|
||||
angle = tool.CuttingEdgeAngle
|
||||
angle = float(tool.CuttingEdgeAngle)
|
||||
if 0 == angle:
|
||||
angle = 180
|
||||
tan = math.tan(math.radians(angle / 2))
|
||||
|
||||
toolDepth = 0 if 0 == tan else width / tan
|
||||
depth = toolDepth + extraDepth
|
||||
toolOffset = tool.FlatRadius
|
||||
extraOffset = tool.Diameter / 2 - width if 180 == angle else extraDepth / tan
|
||||
toolOffset = float(tool.FlatRadius)
|
||||
extraOffset = float(tool.Diameter) / 2 - width if 180 == angle else extraDepth / tan
|
||||
offset = toolOffset + extraOffset
|
||||
return (depth, offset)
|
||||
|
||||
|
||||
@@ -860,10 +860,10 @@ class ObjectDressup:
|
||||
self.toolRadius = 5
|
||||
else:
|
||||
tool = tc.Proxy.getTool(tc) # PathUtils.getTool(obj, tc.ToolNumber)
|
||||
if not tool or tool.Diameter == 0:
|
||||
if not tool or float(tool.Diameter) == 0:
|
||||
self.toolRadius = 5
|
||||
else:
|
||||
self.toolRadius = tool.Diameter / 2
|
||||
self.toolRadius = float(tool.Diameter) / 2
|
||||
|
||||
self.shapes = {}
|
||||
self.dbg = []
|
||||
|
||||
@@ -671,7 +671,7 @@ class PathData:
|
||||
print("tag[%d]" % i)
|
||||
if not i in fromObj.Disabled:
|
||||
dist = self.baseWire.distToShape(Part.Vertex(FreeCAD.Vector(pos.x, pos.y, self.minZ)))
|
||||
if dist[0] < W:
|
||||
if True or dist[0] < W:
|
||||
print("tag[%d/%d]: (%.2f, %.2f, %.2f)" % (i, j, pos.x, pos.y, self.minZ))
|
||||
at = dist[1][0][0]
|
||||
tags.append(Tag(j, at.x, at.y, W, H, A, R, True))
|
||||
@@ -759,7 +759,13 @@ class ObjectTagDressup:
|
||||
obj.addProperty("App::PropertyIntegerList", "Disabled", "Tag", QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "IDs of disabled holding tags"))
|
||||
obj.addProperty("App::PropertyInteger", "SegmentationFactor", "Tag", QtCore.QT_TRANSLATE_NOOP("Path_DressupTag", "Factor determining the # of segments used to approximate rounded tags."))
|
||||
|
||||
self.__setstate__(obj)
|
||||
# for pylint ...
|
||||
self.obj = obj
|
||||
self.solids = []
|
||||
self.tags = []
|
||||
self.pathData = None
|
||||
self.toolRadius = None
|
||||
self.mappers = []
|
||||
|
||||
obj.Proxy = self
|
||||
obj.Base = base
|
||||
@@ -1021,7 +1027,7 @@ class ObjectTagDressup:
|
||||
# traceback.print_exc()
|
||||
return None
|
||||
|
||||
self.toolRadius = PathDressup.toolController(obj.Base).Tool.Diameter / 2
|
||||
self.toolRadius = float(PathDressup.toolController(obj.Base).Tool.Diameter) / 2
|
||||
self.pathData = pathData
|
||||
if generate:
|
||||
obj.Height = self.pathData.defaultTagHeight()
|
||||
|
||||
@@ -220,7 +220,7 @@ class ObjectDressup:
|
||||
PathLog.track()
|
||||
|
||||
def toolRadius(self):
|
||||
return PathDressup.toolController(self.obj.Base).Tool.Diameter / 2.0
|
||||
return float(PathDressup.toolController(self.obj.Base).Tool.Diameter) / 2.0
|
||||
|
||||
def addTagsToDocuemnt(self):
|
||||
for i, solid in enumerate(self.solids):
|
||||
|
||||
@@ -51,6 +51,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
'''Controller for the drilling operation's page'''
|
||||
|
||||
def initPage(self, obj):
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
self.peckDepthSpinBox = PathGui.QuantitySpinBox(self.form.peckDepth, obj, 'PeckDepth')
|
||||
self.peckRetractSpinBox = PathGui.QuantitySpinBox(self.form.peckRetractHeight, obj, 'RetractHeight')
|
||||
self.dwellTimeSpinBox = PathGui.QuantitySpinBox(self.form.dwellTime, obj, 'DwellTime')
|
||||
@@ -80,6 +81,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpDrillingEdit.ui")
|
||||
|
||||
def updateQuantitySpinBoxes(self, index = None):
|
||||
# pylint: disable=unused-argument
|
||||
self.peckDepthSpinBox.updateSpinBox()
|
||||
self.peckRetractSpinBox.updateSpinBox()
|
||||
self.dwellTimeSpinBox.updateSpinBox()
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
import FreeCAD
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
import PySide
|
||||
|
||||
|
||||
@@ -44,34 +45,6 @@ if LOGLEVEL:
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
def _getProperty(obj, prop):
|
||||
o = obj
|
||||
attr = obj
|
||||
name = None
|
||||
for name in prop.split('.'):
|
||||
o = attr
|
||||
if not hasattr(o, name):
|
||||
break
|
||||
attr = getattr(o, name)
|
||||
|
||||
if o == attr:
|
||||
PathLog.warning(translate('PathGui', "%s has no property %s (%s))") % (obj.Label, prop, name))
|
||||
return (None, None, None)
|
||||
|
||||
#PathLog.debug("found property %s of %s (%s: %s)" % (prop, obj.Label, name, attr))
|
||||
return(o, attr, name)
|
||||
|
||||
def getProperty(obj, prop):
|
||||
'''getProperty(obj, prop) ... answer obj's property defined by its canonical name.'''
|
||||
o, attr, name = _getProperty(obj, prop) # pylint: disable=unused-variable
|
||||
return attr
|
||||
|
||||
def setProperty(obj, prop, value):
|
||||
'''setProperty(obj, prop, value) ... set the property value of obj's property defined by its canonical name.'''
|
||||
o, attr, name = _getProperty(obj, prop) # pylint: disable=unused-variable
|
||||
if o and name:
|
||||
setattr(o, name, value)
|
||||
|
||||
def updateInputField(obj, prop, widget, onBeforeChange=None):
|
||||
'''updateInputField(obj, prop, widget) ... update obj's property prop with the value of widget.
|
||||
The property's value is only assigned if the new value differs from the current value.
|
||||
@@ -82,13 +55,13 @@ If onBeforeChange is specified it is called before a new value is assigned to th
|
||||
Returns True if a new value was assigned, False otherwise (new value is the same as the current).
|
||||
'''
|
||||
value = FreeCAD.Units.Quantity(widget.text()).Value
|
||||
attr = getProperty(obj, prop)
|
||||
attr = PathUtil.getProperty(obj, prop)
|
||||
attrValue = attr.Value if hasattr(attr, 'Value') else attr
|
||||
if not PathGeom.isRoughly(attrValue, value):
|
||||
PathLog.debug("updateInputField(%s, %s): %.2f -> %.2f" % (obj.Label, prop, attr, value))
|
||||
if onBeforeChange:
|
||||
onBeforeChange(obj)
|
||||
setProperty(obj, prop, value)
|
||||
PathUtil.setProperty(obj, prop, value)
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -107,7 +80,7 @@ The spin box gets bound to a given property and supports update in both directio
|
||||
self.widget = widget
|
||||
self.prop = prop
|
||||
self.onBeforeChange = onBeforeChange
|
||||
attr = getProperty(self.obj, self.prop)
|
||||
attr = PathUtil.getProperty(self.obj, self.prop)
|
||||
if attr is not None:
|
||||
if hasattr(attr, 'Value'):
|
||||
widget.setProperty('unit', attr.getUserPreferred()[2])
|
||||
@@ -134,7 +107,7 @@ If no value is provided the value of the bound property is used.
|
||||
quantity can be of type Quantity or Float.'''
|
||||
if self.valid:
|
||||
if quantity is None:
|
||||
quantity = getProperty(self.obj, self.prop)
|
||||
quantity = PathUtil.getProperty(self.obj, self.prop)
|
||||
value = quantity.Value if hasattr(quantity, 'Value') else quantity
|
||||
self.widget.setProperty('rawValue', value)
|
||||
|
||||
|
||||
@@ -222,6 +222,7 @@ class ObjectJob:
|
||||
PathLog.debug('taking down tool controller')
|
||||
for tc in obj.ToolController:
|
||||
PathUtil.clearExpressionEngine(tc)
|
||||
tc.Proxy.onDelete(tc)
|
||||
doc.removeObject(tc.Name)
|
||||
obj.ToolController = []
|
||||
# SetupSheet
|
||||
|
||||
@@ -39,13 +39,8 @@ from PySide import QtCore, QtGui
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
LOGLEVEL = False
|
||||
|
||||
if LOGLEVEL:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
class CommandJobCreate:
|
||||
'''
|
||||
@@ -186,5 +181,5 @@ if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('Path_Job', CommandJobCreate())
|
||||
FreeCADGui.addCommand('Path_ExportTemplate', CommandJobTemplateExport())
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathJobGui... done\n")
|
||||
FreeCAD.Console.PrintLog("Loading PathJobCmd... done\n")
|
||||
|
||||
|
||||
@@ -80,7 +80,8 @@ class ObjectFace(PathPocketBase.ObjectPocket):
|
||||
# default depths calculation not correct for facing
|
||||
if prop == "Base":
|
||||
job = PathUtils.findParentJob(obj)
|
||||
obj.OpStartDepth = job.Stock.Shape.BoundBox.ZMax
|
||||
if job:
|
||||
obj.OpStartDepth = job.Stock.Shape.BoundBox.ZMax
|
||||
|
||||
if len(obj.Base) >= 1:
|
||||
print('processing')
|
||||
@@ -95,7 +96,7 @@ class ObjectFace(PathPocketBase.ObjectPocket):
|
||||
# Otherwise, top of part.
|
||||
|
||||
obj.OpFinalDepth = Part.makeCompound(sublist).BoundBox.ZMax
|
||||
else:
|
||||
elif job:
|
||||
obj.OpFinalDepth = job.Proxy.modelBoundBox(job).ZMax
|
||||
|
||||
def areaOpShapes(self, obj):
|
||||
@@ -130,7 +131,7 @@ class ObjectFace(PathPocketBase.ObjectPocket):
|
||||
else:
|
||||
holes.append((b[0].Shape, wire))
|
||||
else:
|
||||
PathLog.error('The base subobject, "{}," is not a face. Ignoring "{}."'.format(sub, sub))
|
||||
PathLog.error('The base subobject, "{0}," is not a face. Ignoring "{0}."'.format(sub))
|
||||
|
||||
if obj.ExcludeRaisedAreas is True and len(holes) > 0:
|
||||
for shape, wire in holes:
|
||||
|
||||
@@ -498,10 +498,10 @@ class ObjectOp(object):
|
||||
self.vertRapid = tc.VertRapid.Value
|
||||
self.horizRapid = tc.HorizRapid.Value
|
||||
tool = tc.Proxy.getTool(tc)
|
||||
if not tool or tool.Diameter == 0:
|
||||
if not tool or float(tool.Diameter) == 0:
|
||||
FreeCAD.Console.PrintError("No Tool found or diameter is zero. We need a tool to build a Path.")
|
||||
return
|
||||
self.radius = tool.Diameter/2
|
||||
self.radius = float(tool.Diameter) /2
|
||||
self.tool = tool
|
||||
obj.OpToolDiameter = tool.Diameter
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -41,6 +41,13 @@ PostProcessorBlacklist = "PostProcessorBlacklist"
|
||||
PostProcessorOutputFile = "PostProcessorOutputFile"
|
||||
PostProcessorOutputPolicy = "PostProcessorOutputPolicy"
|
||||
|
||||
LastPathToolBit = "LastPathToolBit"
|
||||
LastPathToolLibrary = "LastPathToolLibrary"
|
||||
LastPathToolShape = "LastPathToolShape"
|
||||
|
||||
UseLegacyTools = "UseLegacyTools"
|
||||
UseAbsoluteToolPaths = "UseAbsoluteToolPaths"
|
||||
|
||||
# Linear tolerance to use when generating Paths, eg when tessellating geometry
|
||||
GeometryTolerance = "GeometryTolerance"
|
||||
LibAreaCurveAccuracy = "LibAreaCurveAccuarcy"
|
||||
@@ -52,14 +59,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 +117,52 @@ 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 'Shape' == sub:
|
||||
paths.append(lastPathToolShape())
|
||||
|
||||
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 toolsUseLegacyTools():
|
||||
return preferences().GetBool(UseLegacyTools, True)
|
||||
|
||||
def toolsReallyUseLegacyTools():
|
||||
return toolsUseLegacyTools() or not experimentalFeaturesEnabled()
|
||||
|
||||
def toolsStoreAbsolutePaths():
|
||||
return preferences().GetBool(UseAbsoluteToolPaths, False)
|
||||
|
||||
def setToolsSettings(legacy, relative):
|
||||
pref = preferences()
|
||||
pref.SetBool(UseLegacyTools, legacy)
|
||||
pref.SetBool(UseAbsoluteToolPaths, relative)
|
||||
|
||||
def defaultJobTemplate():
|
||||
template = preferences().GetString(DefaultJobTemplate)
|
||||
if 'xml' not in template:
|
||||
@@ -165,3 +216,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 lastPathToolShape():
|
||||
return preferences().GetString(LastPathToolShape, pathDefaultToolsPath('Shape'))
|
||||
def setLastPathToolShape(path):
|
||||
return preferences().SetString(LastPathToolShape, path)
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ class JobPreferencesPage:
|
||||
policy = str(self.form.cboOutputPolicy.currentText())
|
||||
PathPreferences.setOutputFileDefaults(path, policy)
|
||||
self.saveStockSettings()
|
||||
self.saveToolsSettings()
|
||||
|
||||
def saveStockSettings(self):
|
||||
if self.form.stockGroup.isChecked():
|
||||
@@ -107,6 +108,9 @@ class JobPreferencesPage:
|
||||
else:
|
||||
PathPreferences.setDefaultStockTemplate('')
|
||||
|
||||
def saveToolsSettings(self):
|
||||
PathPreferences.setToolsSettings(self.form.toolsUseLegacy.isChecked(), self.form.toolsAbsolutePaths.isChecked())
|
||||
|
||||
def selectComboEntry(self, widget, text):
|
||||
index = widget.findText(text, QtCore.Qt.MatchFixedString)
|
||||
if index >= 0:
|
||||
@@ -167,6 +171,7 @@ class JobPreferencesPage:
|
||||
self.form.tbOutputFile.clicked.connect(self.browseOutputFile)
|
||||
|
||||
self.loadStockSettings()
|
||||
self.loadToolSettings()
|
||||
|
||||
def loadStockSettings(self):
|
||||
stock = PathPreferences.defaultStockTemplate()
|
||||
@@ -244,6 +249,10 @@ class JobPreferencesPage:
|
||||
self.form.stockCreateBox.hide()
|
||||
self.form.stockCreateCylinder.hide()
|
||||
|
||||
def loadToolSettings(self):
|
||||
self.form.toolsUseLegacy.setChecked(PathPreferences.toolsUseLegacyTools())
|
||||
self.form.toolsAbsolutePaths.setChecked(PathPreferences.toolsStoreAbsolutePaths())
|
||||
|
||||
def getPostProcessor(self, name):
|
||||
if not name in self.processor.keys():
|
||||
processor = PostProcessor.load(name)
|
||||
|
||||
@@ -209,11 +209,7 @@ class SetupSheet:
|
||||
for propName in op.properties():
|
||||
prop = OpPropertyName(opName, propName)
|
||||
if hasattr(self.obj, prop):
|
||||
attr = getattr(self.obj, prop)
|
||||
if hasattr(attr, 'UserString'):
|
||||
settings[propName] = attr.UserString
|
||||
else:
|
||||
settings[propName] = attr
|
||||
settings[propName] = PathUtil.getPropertyValueString(self.obj, prop)
|
||||
attrs[opName] = settings
|
||||
|
||||
return attrs
|
||||
|
||||
@@ -306,9 +306,9 @@ class GlobalEditor(object):
|
||||
def getFields(self):
|
||||
def updateExpression(name, widget):
|
||||
value = str(widget.text())
|
||||
val = PathGui.getProperty(self.obj, name)
|
||||
val = PathUtil.getProperty(self.obj, name)
|
||||
if val != value:
|
||||
PathGui.setProperty(self.obj, name, value)
|
||||
PathUtil.setProperty(self.obj, name, value)
|
||||
|
||||
updateExpression('StartDepthExpression', self.form.setupStartDepthExpr)
|
||||
updateExpression('FinalDepthExpression', self.form.setupFinalDepthExpr)
|
||||
|
||||
@@ -106,6 +106,10 @@ class PropertyQuantity(Property):
|
||||
return Property.displayString(self)
|
||||
return self.value.getUserPreferred()[0]
|
||||
|
||||
class PropertyAngle(PropertyQuantity):
|
||||
def typeString(self):
|
||||
return "Angle"
|
||||
|
||||
class PropertyDistance(PropertyQuantity):
|
||||
def typeString(self):
|
||||
return "Distance"
|
||||
@@ -122,14 +126,23 @@ class PropertyFloat(Property):
|
||||
def typeString(self):
|
||||
return "Float"
|
||||
|
||||
def valueFromString(self, string):
|
||||
return float(string)
|
||||
|
||||
class PropertyInteger(Property):
|
||||
def typeString(self):
|
||||
return "Integer"
|
||||
|
||||
def valueFromString(self, string):
|
||||
return int(string)
|
||||
|
||||
class PropertyBool(Property):
|
||||
def typeString(self):
|
||||
return "Bool"
|
||||
|
||||
def valueFromString(self, string):
|
||||
return bool(string)
|
||||
|
||||
class PropertyString(Property):
|
||||
def typeString(self):
|
||||
return "String"
|
||||
@@ -137,24 +150,25 @@ class PropertyString(Property):
|
||||
class OpPrototype(object):
|
||||
|
||||
PropertyType = {
|
||||
'App::PropertyBool': PropertyBool,
|
||||
'App::PropertyDistance': PropertyDistance,
|
||||
'App::PropertyEnumeration': PropertyEnumeration,
|
||||
'App::PropertyFloat': PropertyFloat,
|
||||
'App::PropertyFloatConstraint': Property,
|
||||
'App::PropertyFloatList': Property,
|
||||
'App::PropertyInteger': PropertyInteger,
|
||||
'App::PropertyIntegerList': PropertyInteger,
|
||||
'App::PropertyLength': PropertyLength,
|
||||
'App::PropertyLink': Property,
|
||||
'App::PropertyLinkList': Property,
|
||||
'App::PropertyLinkSubListGlobal': Property,
|
||||
'App::PropertyPercent': PropertyPercent,
|
||||
'App::PropertyString': PropertyString,
|
||||
'App::PropertyStringList': Property,
|
||||
'App::PropertyVectorDistance': Property,
|
||||
'App::PropertyVectorList': Property,
|
||||
'Part::PropertyPartShape': Property,
|
||||
'App::PropertyAngle': PropertyAngle,
|
||||
'App::PropertyBool': PropertyBool,
|
||||
'App::PropertyDistance': PropertyDistance,
|
||||
'App::PropertyEnumeration': PropertyEnumeration,
|
||||
'App::PropertyFloat': PropertyFloat,
|
||||
'App::PropertyFloatConstraint': Property,
|
||||
'App::PropertyFloatList': Property,
|
||||
'App::PropertyInteger': PropertyInteger,
|
||||
'App::PropertyIntegerList': PropertyInteger,
|
||||
'App::PropertyLength': PropertyLength,
|
||||
'App::PropertyLink': Property,
|
||||
'App::PropertyLinkList': Property,
|
||||
'App::PropertyLinkSubListGlobal': Property,
|
||||
'App::PropertyPercent': PropertyPercent,
|
||||
'App::PropertyString': PropertyString,
|
||||
'App::PropertyStringList': Property,
|
||||
'App::PropertyVectorDistance': Property,
|
||||
'App::PropertyVectorList': Property,
|
||||
'Part::PropertyPartShape': Property,
|
||||
}
|
||||
|
||||
def __init__(self, name):
|
||||
|
||||
@@ -112,6 +112,21 @@ class _PropertyStringEditor(_PropertyEditor):
|
||||
def setModelData(self, widget):
|
||||
self.prop.setValue(widget.text())
|
||||
|
||||
class _PropertyAngleEditor(_PropertyEditor):
|
||||
'''Editor for angle values - uses a line edit'''
|
||||
|
||||
def widget(self, parent):
|
||||
return QtGui.QLineEdit(parent)
|
||||
|
||||
def setEditorData(self, widget):
|
||||
quantity = self.prop.getValue()
|
||||
if quantity is None:
|
||||
quantity = FreeCAD.Units.Quantity(0, FreeCAD.Units.Angle)
|
||||
widget.setText(quantity.getUserPreferred()[0])
|
||||
|
||||
def setModelData(self, widget):
|
||||
self.prop.setValue(FreeCAD.Units.Quantity(widget.text()))
|
||||
|
||||
class _PropertyLengthEditor(_PropertyEditor):
|
||||
'''Editor for length values - uses a line edit.'''
|
||||
|
||||
@@ -174,15 +189,16 @@ class _PropertyFloatEditor(_PropertyEditor):
|
||||
self.prop.setValue(widget.value())
|
||||
|
||||
_EditorFactory = {
|
||||
PathSetupSheetOpPrototype.Property: None,
|
||||
PathSetupSheetOpPrototype.PropertyBool: _PropertyBoolEditor,
|
||||
PathSetupSheetOpPrototype.PropertyDistance: _PropertyLengthEditor,
|
||||
PathSetupSheetOpPrototype.PropertyEnumeration: _PropertyEnumEditor,
|
||||
PathSetupSheetOpPrototype.PropertyFloat: _PropertyFloatEditor,
|
||||
PathSetupSheetOpPrototype.PropertyInteger: _PropertyIntegerEditor,
|
||||
PathSetupSheetOpPrototype.PropertyLength: _PropertyLengthEditor,
|
||||
PathSetupSheetOpPrototype.PropertyPercent: _PropertyPercentEditor,
|
||||
PathSetupSheetOpPrototype.PropertyString: _PropertyStringEditor,
|
||||
PathSetupSheetOpPrototype.Property: None,
|
||||
PathSetupSheetOpPrototype.PropertyAngle: _PropertyAngleEditor,
|
||||
PathSetupSheetOpPrototype.PropertyBool: _PropertyBoolEditor,
|
||||
PathSetupSheetOpPrototype.PropertyDistance: _PropertyLengthEditor,
|
||||
PathSetupSheetOpPrototype.PropertyEnumeration: _PropertyEnumEditor,
|
||||
PathSetupSheetOpPrototype.PropertyFloat: _PropertyFloatEditor,
|
||||
PathSetupSheetOpPrototype.PropertyInteger: _PropertyIntegerEditor,
|
||||
PathSetupSheetOpPrototype.PropertyLength: _PropertyLengthEditor,
|
||||
PathSetupSheetOpPrototype.PropertyPercent: _PropertyPercentEditor,
|
||||
PathSetupSheetOpPrototype.PropertyString: _PropertyStringEditor,
|
||||
}
|
||||
|
||||
def Editor(prop):
|
||||
|
||||
@@ -125,7 +125,7 @@ class PathSimulation:
|
||||
# if hasattr(self.operation, "ToolController"):
|
||||
# self.tool = self.operation.ToolController.Tool
|
||||
if (self.tool is not None):
|
||||
toolProf = self.CreateToolProfile(self.tool, Vector(0, 1, 0), Vector(0, 0, 0), self.tool.Diameter / 2.0)
|
||||
toolProf = self.CreateToolProfile(self.tool, Vector(0, 1, 0), Vector(0, 0, 0), float(self.tool.Diameter) / 2.0)
|
||||
self.cutTool.Shape = Part.makeSolid(toolProf.revolve(Vector(0, 0, 0), Vector(0, 0, 1)))
|
||||
self.cutTool.ViewObject.show()
|
||||
self.voxSim.SetCurrentTool(self.tool)
|
||||
@@ -298,7 +298,7 @@ class PathSimulation:
|
||||
# except:
|
||||
# return (None, e1.valueAt(e1.LastParameter))
|
||||
# height = self.height
|
||||
# rad = tool.Diameter / 2.0 - 0.001 * curpos[2] # hack to overcome occ bug
|
||||
# rad = float(tool.Diameter) / 2.0 - 0.001 * curpos[2] # hack to overcome occ bug
|
||||
# if type(e1.Curve) is Part.Circle and e1.Curve.Radius <= rad: # hack to overcome occ bug
|
||||
# rad = e1.Curve.Radius - 0.001
|
||||
# # return (None, e1.valueAt(e1.LastParameter))
|
||||
@@ -350,7 +350,7 @@ class PathSimulation:
|
||||
# height = self.height
|
||||
|
||||
# hack to overcome occ bugs
|
||||
rad = tool.Diameter / 2.0 - 0.001 * pos[2]
|
||||
rad = float(tool.Diameter) / 2.0 - 0.001 * pos[2]
|
||||
# rad = rad + 0.001 * self.icmd
|
||||
if type(toolPath.Curve) is Part.Circle and toolPath.Curve.Radius <= rad:
|
||||
rad = toolPath.Curve.Radius - 0.01 * (pos[2] + 1)
|
||||
@@ -386,7 +386,7 @@ class PathSimulation:
|
||||
# create radial profile of the tool (90 degrees to the direction of the path)
|
||||
def CreateToolProfile(self, tool, dir, pos, rad):
|
||||
type = tool.ToolType
|
||||
# rad = tool.Diameter / 2.0 - 0.001 * pos[2] # hack to overcome occ bug
|
||||
# rad = float(tool.Diameter) / 2.0 - 0.001 * pos[2] # hack to overcome occ bug
|
||||
xf = dir[0] * rad
|
||||
yf = dir[1] * rad
|
||||
xp = pos[0]
|
||||
|
||||
@@ -1792,10 +1792,11 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
def setOclCutter(self, obj):
|
||||
# Set cutter details
|
||||
# https://www.freecadweb.org/api/dd/dfe/classPath_1_1Tool.html#details
|
||||
diam_1 = obj.ToolController.Tool.Diameter
|
||||
lenOfst = obj.ToolController.Tool.LengthOffset
|
||||
FR = obj.ToolController.Tool.FlatRadius
|
||||
CEH = obj.ToolController.Tool.CuttingEdgeHeight
|
||||
diam_1 = float(obj.ToolController.Tool.Diameter)
|
||||
lenOfst = obj.ToolController.Tool.LengthOffset if hasattr(obj.ToolController.Tool, 'LengthOffset') else 0
|
||||
FR = obj.ToolController.Tool.FlatRadius if hasattr(obj.ToolController.Tool, 'FlatRadius') else 0
|
||||
CEH = obj.ToolController.Tool.CuttingEdgeHeight if hasattr(obj.ToolController.Tool, 'CuttingEdgeHeight') else 0
|
||||
CEA = obj.ToolController.Tool.CuttingEdgeAngle if hasattr(obj.ToolController.Tool, 'CuttingEdgeAngle') else 0
|
||||
|
||||
if obj.ToolController.Tool.ToolType == 'EndMill':
|
||||
# Standard End Mill
|
||||
@@ -1817,13 +1818,13 @@ class ObjectSurface(PathOp.ObjectOp):
|
||||
# Bull Nose or Corner Radius cutter
|
||||
# Reference: https://www.fine-tools.com/halbstabfraeser.html
|
||||
# OCL -> ConeCutter::ConeCutter(diameter, angle, lengthOffset)
|
||||
self.cutter = ocl.ConeCutter(diam_1, (obj.ToolController.Tool.CuttingEdgeAngle / 2), lenOfst)
|
||||
self.cutter = ocl.ConeCutter(diam_1, (CEA / 2), lenOfst)
|
||||
|
||||
elif obj.ToolController.Tool.ToolType == 'ChamferMill':
|
||||
# Bull Nose or Corner Radius cutter
|
||||
# Reference: https://www.fine-tools.com/halbstabfraeser.html
|
||||
# OCL -> ConeCutter::ConeCutter(diameter, angle, lengthOffset)
|
||||
self.cutter = ocl.ConeCutter(diam_1, (obj.ToolController.Tool.CuttingEdgeAngle / 2), lenOfst)
|
||||
self.cutter = ocl.ConeCutter(diam_1, (CEA / 2), lenOfst)
|
||||
else:
|
||||
# Default to standard end mill
|
||||
self.cutter = ocl.CylCutter(diam_1, (CEH + lenOfst))
|
||||
|
||||
383
src/Mod/Path/PathScripts/PathToolBit.py
Normal file
383
src/Mod/Path/PathScripts/PathToolBit.py
Normal file
@@ -0,0 +1,383 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2019 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * 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 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."
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
__doc__ = "Class to deal with and represent a tool bit."
|
||||
|
||||
#PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
#PathLog.trackModule()
|
||||
|
||||
def translate(context, text, disambig=None):
|
||||
return PySide.QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
ParameterTypeConstraint = {
|
||||
'Angle': 'App::PropertyAngle',
|
||||
'Distance': 'App::PropertyLength',
|
||||
'DistanceX': 'App::PropertyLength',
|
||||
'DistanceY': 'App::PropertyLength',
|
||||
'Radius': 'App::PropertyLength'
|
||||
}
|
||||
|
||||
|
||||
def _findTool(path, typ, dbg=False):
|
||||
if os.path.exists(path):
|
||||
if dbg:
|
||||
PathLog.debug("Found {} at {}".format(typ, path))
|
||||
return path
|
||||
|
||||
def searchFor(pname, fname):
|
||||
if dbg:
|
||||
PathLog.debug("Looking for {}".format(pname))
|
||||
if fname:
|
||||
for p in PathPreferences.searchPathsTool(typ):
|
||||
f = os.path.join(p, fname)
|
||||
if dbg:
|
||||
PathLog.debug(" Checking {}".format(f))
|
||||
if os.path.exists(f):
|
||||
if dbg:
|
||||
PathLog.debug(" Found {} at {}".format(typ, f))
|
||||
return f
|
||||
if pname and os.path.sep != 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 findShape(path):
|
||||
'''findShape(path) ... search for path, full and partially in all known shape directories.'''
|
||||
return _findTool(path, 'Shape')
|
||||
|
||||
def findBit(path):
|
||||
if path.endswith('.fctb'):
|
||||
return _findTool(path, 'Bit')
|
||||
return _findTool("{}.fctb".format(path), 'Bit')
|
||||
|
||||
def findLibrary(path, dbg=False):
|
||||
if path.endswith('.fctl'):
|
||||
return _findTool(path, 'Library', dbg)
|
||||
return _findTool("{}.fctl".format(path), 'Library', dbg)
|
||||
|
||||
def _findRelativePath(path, typ):
|
||||
relative = path
|
||||
for p in PathPreferences.searchPathsTool(typ):
|
||||
if path.startswith(p):
|
||||
p = path[len(p):]
|
||||
if os.path.sep == p[0]:
|
||||
p = p[1:]
|
||||
if len(p) < len(relative):
|
||||
relative = p
|
||||
return relative
|
||||
|
||||
def findRelativePathShape(path):
|
||||
return _findRelativePath(path, 'Shape')
|
||||
|
||||
def findRelativePathTool(path):
|
||||
return _findRelativePath(path, 'Bit')
|
||||
|
||||
def findRelativePathLibrary(path):
|
||||
return _findRelativePath(path, 'Library')
|
||||
|
||||
def updateConstraint(sketch, name, value):
|
||||
for i, constraint in enumerate(sketch.Constraints):
|
||||
if constraint.Name.split(';')[0] == name:
|
||||
constr = None
|
||||
if constraint.Type in ['DistanceX', 'DistanceY', 'Distance', 'Radius', 'Angle']:
|
||||
constr = Sketcher.Constraint(constraint.Type, constraint.First, constraint.FirstPos, constraint.Second, constraint.SecondPos, value)
|
||||
else:
|
||||
print(constraint.Name, constraint.Type)
|
||||
|
||||
if constr is not None:
|
||||
if not PathGeom.isRoughly(constraint.Value, value.Value):
|
||||
PathLog.track(name, constraint.Type, 'update', i, "(%.2f -> %.2f)" % (constraint.Value, value.Value))
|
||||
sketch.delConstraint(i)
|
||||
sketch.recompute()
|
||||
n = sketch.addConstraint(constr)
|
||||
sketch.renameConstraint(n, constraint.Name)
|
||||
else:
|
||||
PathLog.track(name, constraint.Type, 'unchanged')
|
||||
break
|
||||
|
||||
|
||||
PropertyGroupBit = 'Bit'
|
||||
PropertyGroupAttribute = 'Attribute'
|
||||
|
||||
class ToolBit(object):
|
||||
|
||||
def __init__(self, obj, shapeFile):
|
||||
PathLog.track(obj.Label, shapeFile)
|
||||
self.obj = obj
|
||||
obj.addProperty('App::PropertyFile', 'BitShape', 'Base', translate('PathToolBit', 'Shape for bit shape'))
|
||||
obj.addProperty('App::PropertyLink', 'BitBody', 'Base', translate('PathToolBit', 'The parametrized body representing the tool bit'))
|
||||
obj.addProperty('App::PropertyFile', 'File', 'Base', translate('PathToolBit', 'The file of the tool'))
|
||||
if shapeFile is None:
|
||||
obj.BitShape = 'endmill.fcstd'
|
||||
self._setupBitShape(obj)
|
||||
self.unloadBitBody(obj)
|
||||
else:
|
||||
obj.BitShape = shapeFile
|
||||
self._setupBitShape(obj)
|
||||
self.onDocumentRestored(obj)
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
for obj in FreeCAD.ActiveDocument.Objects:
|
||||
if hasattr(obj, 'Proxy') and obj.Proxy == self:
|
||||
self.obj = obj
|
||||
break
|
||||
return None
|
||||
|
||||
def propertyNamesBit(self, obj):
|
||||
return [prop for prop in obj.PropertiesList if obj.getGroupOfProperty(prop) == PropertyGroupBit]
|
||||
|
||||
def propertyNamesAttribute(self, obj):
|
||||
return [prop for prop in obj.PropertiesList if obj.getGroupOfProperty(prop) == PropertyGroupAttribute]
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
obj.setEditorMode('BitShape', 1)
|
||||
obj.setEditorMode('BitBody', 2)
|
||||
obj.setEditorMode('File', 1)
|
||||
obj.setEditorMode('Shape', 2)
|
||||
|
||||
for prop in self.propertyNamesBit(obj):
|
||||
obj.setEditorMode(prop, 1)
|
||||
# I currently don't see why these need to be read-only
|
||||
#for prop in self.propertyNamesAttribute(obj):
|
||||
# obj.setEditorMode(prop, 1)
|
||||
|
||||
def onChanged(self, obj, prop):
|
||||
PathLog.track(obj.Label, prop)
|
||||
if prop == 'BitShape' and not 'Restore' in obj.State:
|
||||
self._setupBitShape(obj)
|
||||
#elif obj.getGroupOfProperty(prop) == PropertyGroupBit:
|
||||
# self._updateBitShape(obj, [prop])
|
||||
|
||||
def onDelete(self, obj, arg2=None):
|
||||
PathLog.track(obj.Label)
|
||||
self.unloadBitBody(obj)
|
||||
|
||||
def _updateBitShape(self, obj, properties=None):
|
||||
if not obj.BitBody is None:
|
||||
if not properties:
|
||||
properties = self.propertyNamesBit(obj)
|
||||
for prop in properties:
|
||||
for sketch in [o for o in obj.BitBody.Group if o.TypeId == 'Sketcher::SketchObject']:
|
||||
PathLog.track(obj.Label, sketch.Label, prop)
|
||||
updateConstraint(sketch, prop, obj.getPropertyByName(prop))
|
||||
self._copyBitShape(obj)
|
||||
|
||||
def _copyBitShape(self, obj):
|
||||
obj.Document.recompute()
|
||||
if obj.BitBody and obj.BitBody.Shape:
|
||||
obj.Shape = obj.BitBody.Shape
|
||||
else:
|
||||
obj.Shape = Part.Shape()
|
||||
|
||||
def _loadBitBody(self, obj, path=None):
|
||||
p = path if path else obj.BitShape
|
||||
docOpened = False
|
||||
doc = None
|
||||
for d in FreeCAD.listDocuments():
|
||||
if FreeCAD.getDocument(d).FileName == p:
|
||||
doc = FreeCAD.getDocument(d)
|
||||
break
|
||||
if doc is None:
|
||||
p = findShape(p)
|
||||
if not path and p != obj.BitShape:
|
||||
obj.BitShape = p
|
||||
doc = FreeCAD.open(p)
|
||||
docOpened = True
|
||||
return (doc, docOpened)
|
||||
|
||||
def _removeBitBody(self, obj):
|
||||
if obj.BitBody:
|
||||
obj.BitBody.removeObjectsFromDocument()
|
||||
obj.Document.removeObject(obj.BitBody.Name)
|
||||
obj.BitBody = None
|
||||
|
||||
def _deleteBitSetup(self, obj):
|
||||
PathLog.track(obj.Label)
|
||||
self._removeBitBody(obj)
|
||||
self._copyBitShape(obj)
|
||||
for prop in self.propertyNamesBit(obj):
|
||||
obj.removeProperty(prop)
|
||||
|
||||
def loadBitBody(self, obj, force=False):
|
||||
if force or not obj.BitBody:
|
||||
activeDoc = FreeCAD.ActiveDocument
|
||||
if force:
|
||||
self._removeBitBody(obj)
|
||||
(doc, opened) = self._loadBitBody(obj)
|
||||
obj.BitBody = obj.Document.copyObject(doc.RootObjects[0], True)
|
||||
if opened:
|
||||
FreeCAD.setActiveDocument(activeDoc.Name)
|
||||
FreeCAD.closeDocument(doc.Name)
|
||||
self._updateBitShape(obj)
|
||||
|
||||
def unloadBitBody(self, obj):
|
||||
self._removeBitBody(obj)
|
||||
|
||||
def _setupBitShape(self, obj, path=None):
|
||||
activeDoc = FreeCAD.ActiveDocument
|
||||
(doc, docOpened) = self._loadBitBody(obj, path)
|
||||
|
||||
obj.Label = doc.RootObjects[0].Label
|
||||
self._deleteBitSetup(obj)
|
||||
obj.BitBody = obj.Document.copyObject(doc.RootObjects[0], True)
|
||||
if docOpened:
|
||||
FreeCAD.setActiveDocument(activeDoc.Name)
|
||||
FreeCAD.closeDocument(doc.Name)
|
||||
|
||||
if obj.BitBody.ViewObject:
|
||||
obj.BitBody.ViewObject.Visibility = False
|
||||
self._copyBitShape(obj)
|
||||
|
||||
for sketch in [o for o in obj.BitBody.Group if o.TypeId == 'Sketcher::SketchObject']:
|
||||
for constraint in [c for c in sketch.Constraints if c.Name != '']:
|
||||
typ = ParameterTypeConstraint.get(constraint.Type)
|
||||
PathLog.track(constraint, typ)
|
||||
if typ is not None:
|
||||
parts = [p.strip() for p in constraint.Name.split(';')]
|
||||
prop = parts[0]
|
||||
desc = ''
|
||||
if len(parts) > 1:
|
||||
desc = parts[1]
|
||||
obj.addProperty(typ, prop, PropertyGroupBit, desc)
|
||||
obj.setEditorMode(prop, 1)
|
||||
value = constraint.Value
|
||||
if constraint.Type == 'Angle':
|
||||
value = value * 180 / math.pi
|
||||
PathUtil.setProperty(obj, prop, value)
|
||||
|
||||
def getBitThumbnail(self, obj):
|
||||
if obj.BitShape:
|
||||
path = findShape(obj.BitShape)
|
||||
if path:
|
||||
with open(path, 'rb') as fd:
|
||||
zf = zipfile.ZipFile(fd)
|
||||
pf = zf.open('thumbnails/Thumbnail.png', 'r')
|
||||
data = pf.read()
|
||||
pf.close()
|
||||
return data
|
||||
return None
|
||||
|
||||
def saveToFile(self, obj, path, setFile=True):
|
||||
try:
|
||||
with open(path, 'w') as fp:
|
||||
json.dump(self.shapeAttrs(obj), fp, indent=' ')
|
||||
if setFile:
|
||||
obj.File = path
|
||||
return True
|
||||
except (OSError, IOError) as e:
|
||||
PathLog.error("Could not save tool %s to %s (%s)" % (obj.Label, path, e))
|
||||
raise
|
||||
|
||||
def shapeAttrs(self, obj):
|
||||
attrs = {}
|
||||
attrs['version'] = 2 # Path.Tool is version 1
|
||||
attrs['name'] = obj.Label
|
||||
if PathPreferences.toolsStoreAbsolutePaths():
|
||||
attrs['shape'] = obj.BitShape
|
||||
else:
|
||||
attrs['shape'] = findRelativePathShape(obj.BitShape)
|
||||
params = {}
|
||||
for name in self.propertyNamesBit(obj):
|
||||
params[name] = PathUtil.getPropertyValueString(obj, name)
|
||||
attrs['parameter'] = params
|
||||
params = {}
|
||||
for name in self.propertyNamesAttribute(obj):
|
||||
params[name] = PathUtil.getPropertyValueString(obj, name)
|
||||
attrs['attribute'] = params
|
||||
return attrs
|
||||
|
||||
def Declaration(path):
|
||||
with open(path, 'r') as fp:
|
||||
return json.load(fp)
|
||||
|
||||
class AttributePrototype(PathSetupSheetOpPrototype.OpPrototype):
|
||||
|
||||
def __init__(self):
|
||||
PathSetupSheetOpPrototype.OpPrototype.__init__(self, 'ToolBitAttribute')
|
||||
self.addProperty('App::PropertyEnumeration', 'Material', PropertyGroupAttribute, translate('PathToolBit', 'Tool bit material'))
|
||||
self.Material = ['Carbide', 'CastAlloy', 'Ceramics', 'Diamond', 'HighCarbonToolSteel', 'HighSpeedSteel', 'Sialon']
|
||||
self.addProperty('App::PropertyDistance', 'LengthOffset', PropertyGroupAttribute, translate('PathToolBit', 'Length offset in Z direction'))
|
||||
self.addProperty('App::PropertyInteger', 'Flutes', PropertyGroupAttribute, translate('PathToolBit', 'The number of flutes'))
|
||||
self.addProperty('App::PropertyDistance', 'ChipLoad', PropertyGroupAttribute, translate('PathToolBit', 'Chipload as per manufacturer'))
|
||||
|
||||
|
||||
class ToolBitFactory(object):
|
||||
|
||||
def CreateFromAttrs(self, attrs, name='ToolBit'):
|
||||
# pylint: disable=protected-access
|
||||
obj = Factory.Create(name, attrs['shape'])
|
||||
obj.Label = attrs['name']
|
||||
params = attrs['parameter']
|
||||
for prop in params:
|
||||
PathUtil.setProperty(obj, prop, params[prop])
|
||||
obj.Proxy._updateBitShape(obj)
|
||||
obj.Proxy.unloadBitBody(obj)
|
||||
params = attrs['attribute']
|
||||
proto = AttributePrototype()
|
||||
for pname in params:
|
||||
prop = proto.getProperty(pname)
|
||||
val = prop.valueFromString(params[pname])
|
||||
print("prop[%s] = %s (%s)" % (pname, params[pname], type(val)))
|
||||
prop.setupProperty(obj, pname, PropertyGroupAttribute, prop.valueFromString(params[pname]))
|
||||
return obj
|
||||
|
||||
def CreateFrom(self, path, name='ToolBit'):
|
||||
try:
|
||||
data = Declaration(path)
|
||||
bit = Factory.CreateFromAttrs(data, name)
|
||||
bit.File = path
|
||||
return bit
|
||||
except (OSError, IOError) as e:
|
||||
PathLog.error("%s not a valid tool file (%s)" % (path, e))
|
||||
raise
|
||||
|
||||
def Create(self, name='ToolBit', shapeFile=None):
|
||||
obj = FreeCAD.ActiveDocument.addObject('Part::FeaturePython', name)
|
||||
obj.Proxy = ToolBit(obj, shapeFile)
|
||||
return obj
|
||||
|
||||
Factory = ToolBitFactory()
|
||||
139
src/Mod/Path/PathScripts/PathToolBitCmd.py
Normal file
139
src/Mod/Path/PathScripts/PathToolBitCmd.py
Normal file
@@ -0,0 +1,139 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2019 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * 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
|
||||
import os
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
class CommandToolBitCreate:
|
||||
'''
|
||||
Command used to create a new Tool.
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path-ToolBit',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Create Tool"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Creates a new ToolBit object")}
|
||||
|
||||
def IsActive(self):
|
||||
return FreeCAD.ActiveDocument is not None
|
||||
|
||||
def Activated(self):
|
||||
obj = PathScripts.PathToolBit.Factory.Create()
|
||||
obj.ViewObject.Proxy.setCreate(obj.ViewObject)
|
||||
|
||||
class CommandToolBitSave:
|
||||
'''
|
||||
Command used to save an existing Tool to a file.
|
||||
'''
|
||||
|
||||
def __init__(self, saveAs):
|
||||
self.saveAs = saveAs
|
||||
|
||||
def GetResources(self):
|
||||
if self.saveAs:
|
||||
menuTxt = QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Save Tool as...")
|
||||
else:
|
||||
menuTxt = QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Save Tool")
|
||||
return {'Pixmap': 'Path-ToolBit',
|
||||
'MenuText': menuTxt,
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Save an existing ToolBit object to a file")}
|
||||
|
||||
def selectedTool(self):
|
||||
sel = FreeCADGui.Selection.getSelectionEx()
|
||||
if 1 == len(sel) and isinstance(sel[0].Object.Proxy, PathScripts.PathToolBit.ToolBit):
|
||||
return sel[0].Object
|
||||
return None
|
||||
|
||||
def IsActive(self):
|
||||
tool = self.selectedTool()
|
||||
if tool:
|
||||
if tool.File:
|
||||
return True
|
||||
return self.saveAs
|
||||
return False
|
||||
|
||||
def Activated(self):
|
||||
from PySide import QtGui
|
||||
tool = self.selectedTool()
|
||||
if tool:
|
||||
path = None
|
||||
if not tool.File or self.saveAs:
|
||||
if tool.File:
|
||||
fname = tool.File
|
||||
else:
|
||||
fname = os.path.join(PathScripts.PathPreferences.lastPathToolBit(), tool.Label + '.fctb')
|
||||
foo = QtGui.QFileDialog.getSaveFileName(QtGui.QApplication.activeWindow(), "Tool", fname, "*.fctb")
|
||||
if foo:
|
||||
path = foo[0]
|
||||
else:
|
||||
path = tool.File
|
||||
|
||||
if path:
|
||||
if not path.endswith('.fctb'):
|
||||
path += '.fctb'
|
||||
tool.Proxy.saveToFile(tool, path)
|
||||
PathScripts.PathPreferences.setLastPathToolBit(os.path.dirname(path))
|
||||
|
||||
class CommandToolBitLoad:
|
||||
'''
|
||||
Command used to load an existing Tool from a file into the current document.
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path-ToolBit',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Load Tool"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathToolBit", "Load an existing ToolBit object from a file")}
|
||||
|
||||
def selectedTool(self):
|
||||
sel = FreeCADGui.Selection.getSelectionEx()
|
||||
if 1 == len(sel) and isinstance(sel[0].Object.Proxy, PathScripts.PathToolBit.ToolBit):
|
||||
return sel[0].Object
|
||||
return None
|
||||
|
||||
def IsActive(self):
|
||||
return FreeCAD.ActiveDocument is not None
|
||||
|
||||
def Activated(self):
|
||||
if PathScripts.PathToolBitGui.LoadTools():
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('Path_ToolBitCreate', CommandToolBitCreate())
|
||||
FreeCADGui.addCommand('Path_ToolBitLoad', CommandToolBitLoad())
|
||||
FreeCADGui.addCommand('Path_ToolBitSave', CommandToolBitSave(False))
|
||||
FreeCADGui.addCommand('Path_ToolBitSaveAs', CommandToolBitSave(True))
|
||||
|
||||
CommandList = ['Path_ToolBitCreate', 'Path_ToolBitLoad', 'Path_ToolBitSave', 'Path_ToolBitSaveAs']
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathToolBitCmd... done\n")
|
||||
197
src/Mod/Path/PathScripts/PathToolBitEdit.py
Normal file
197
src/Mod/Path/PathScripts/PathToolBitEdit.py
Normal file
@@ -0,0 +1,197 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2019 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * 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 FreeCADGui
|
||||
import PathScripts.PathGui as PathGui
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathSetupSheetGui as PathSetupSheetGui
|
||||
import PathScripts.PathToolBit as PathToolBit
|
||||
import os
|
||||
import re
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
#PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
class ToolBitEditor(object):
|
||||
'''UI and controller for editing a ToolBit.
|
||||
The controller embeds the UI to the parentWidget which has to have a layout attached to it.
|
||||
'''
|
||||
|
||||
def __init__(self, tool, parentWidget=None):
|
||||
self.form = FreeCADGui.PySideUic.loadUi(":/panels/ToolBitEditor.ui")
|
||||
|
||||
if parentWidget:
|
||||
self.form.setParent(parentWidget)
|
||||
parentWidget.layout().addWidget(self.form)
|
||||
|
||||
self.tool = tool
|
||||
if not tool.BitShape:
|
||||
self.tool.BitShape = 'endmill.fcstd'
|
||||
self.tool.Proxy.loadBitBody(self.tool)
|
||||
self.setupTool(self.tool)
|
||||
self.setupAttributes(self.tool)
|
||||
|
||||
def setupTool(self, tool):
|
||||
layout = self.form.bitParams.layout()
|
||||
for i in range(layout.rowCount() - 1, -1, -1):
|
||||
layout.removeRow(i)
|
||||
editor = {}
|
||||
ui = FreeCADGui.UiLoader()
|
||||
for name in tool.PropertiesList:
|
||||
if tool.getGroupOfProperty(name) == PathToolBit.PropertyGroupBit:
|
||||
qsb = ui.createWidget('Gui::QuantitySpinBox')
|
||||
editor[name] = PathGui.QuantitySpinBox(qsb, tool, name)
|
||||
label = QtGui.QLabel(re.sub('([A-Z][a-z]+)', r' \1', re.sub('([A-Z]+)', r' \1', name)))
|
||||
#if parameter.get('Desc'):
|
||||
# qsb.setToolTip(parameter['Desc'])
|
||||
layout.addRow(label, qsb)
|
||||
self.bitEditor = editor
|
||||
img = tool.Proxy.getBitThumbnail(tool)
|
||||
if img:
|
||||
self.form.image.setPixmap(QtGui.QPixmap(QtGui.QImage.fromData(img)))
|
||||
else:
|
||||
self.form.image.setPixmap(QtGui.QPixmap())
|
||||
|
||||
def setupAttributes(self, tool):
|
||||
self.proto = PathToolBit.AttributePrototype()
|
||||
self.props = sorted(self.proto.properties)
|
||||
self.delegate = PathSetupSheetGui.Delegate(self.form)
|
||||
self.model = QtGui.QStandardItemModel(len(self.props), 3, self.form)
|
||||
self.model.setHorizontalHeaderLabels(['Set', 'Property', 'Value'])
|
||||
|
||||
for i, name in enumerate(self.props):
|
||||
prop = self.proto.getProperty(name)
|
||||
isset = hasattr(tool, name)
|
||||
if isset:
|
||||
prop.setValue(getattr(tool, name))
|
||||
|
||||
self.model.setData(self.model.index(i, 0), isset, QtCore.Qt.EditRole)
|
||||
self.model.setData(self.model.index(i, 1), name, QtCore.Qt.EditRole)
|
||||
self.model.setData(self.model.index(i, 2), prop, PathSetupSheetGui.Delegate.PropertyRole)
|
||||
self.model.setData(self.model.index(i, 2), prop.displayString(), QtCore.Qt.DisplayRole)
|
||||
|
||||
self.model.item(i, 0).setCheckable(True)
|
||||
self.model.item(i, 0).setText('')
|
||||
self.model.item(i, 1).setEditable(False)
|
||||
self.model.item(i, 1).setToolTip(prop.info)
|
||||
self.model.item(i, 2).setToolTip(prop.info)
|
||||
|
||||
if isset:
|
||||
self.model.item(i, 0).setCheckState(QtCore.Qt.Checked)
|
||||
else:
|
||||
self.model.item(i, 0).setCheckState(QtCore.Qt.Unchecked)
|
||||
self.model.item(i, 1).setEnabled(False)
|
||||
self.model.item(i, 2).setEnabled(False)
|
||||
|
||||
self.form.attrTable.setModel(self.model)
|
||||
self.form.attrTable.setItemDelegateForColumn(2, self.delegate)
|
||||
self.form.attrTable.resizeColumnsToContents()
|
||||
self.form.attrTable.verticalHeader().hide()
|
||||
|
||||
self.model.dataChanged.connect(self.updateData)
|
||||
|
||||
def updateData(self, topLeft, bottomRight):
|
||||
# pylint: disable=unused-argument
|
||||
if 0 == topLeft.column():
|
||||
isset = self.model.item(topLeft.row(), 0).checkState() == QtCore.Qt.Checked
|
||||
self.model.item(topLeft.row(), 1).setEnabled(isset)
|
||||
self.model.item(topLeft.row(), 2).setEnabled(isset)
|
||||
|
||||
def accept(self):
|
||||
self.refresh()
|
||||
self.tool.Proxy.unloadBitBody(self.tool)
|
||||
|
||||
# get the attributes
|
||||
for i, name in enumerate(self.props):
|
||||
prop = self.proto.getProperty(name)
|
||||
enabled = self.model.item(i, 0).checkState() == QtCore.Qt.Checked
|
||||
if enabled and not prop.getValue() is None:
|
||||
prop.setupProperty(self.tool, name, PathToolBit.PropertyGroupAttribute, prop.getValue())
|
||||
elif hasattr(self.tool, name):
|
||||
self.tool.removeProperty(name)
|
||||
|
||||
def reject(self):
|
||||
self.tool.Proxy.unloadBitBody(self.tool)
|
||||
|
||||
def updateUI(self):
|
||||
PathLog.track()
|
||||
self.form.toolName.setText(self.tool.Label)
|
||||
self.form.shapePath.setText(self.tool.BitShape)
|
||||
|
||||
for editor in self.bitEditor:
|
||||
self.bitEditor[editor].updateSpinBox()
|
||||
|
||||
def updateShape(self):
|
||||
self.tool.BitShape = str(self.form.shapePath.text())
|
||||
self.setupTool(self.tool)
|
||||
self.form.toolName.setText(self.tool.Label)
|
||||
|
||||
for editor in self.bitEditor:
|
||||
self.bitEditor[editor].updateSpinBox()
|
||||
|
||||
def updateTool(self):
|
||||
# pylint: disable=protected-access
|
||||
PathLog.track()
|
||||
self.tool.Label = str(self.form.toolName.text())
|
||||
self.tool.BitShape = str(self.form.shapePath.text())
|
||||
|
||||
for editor in self.bitEditor:
|
||||
self.bitEditor[editor].updateProperty()
|
||||
|
||||
self.tool.Proxy._updateBitShape(self.tool)
|
||||
|
||||
def refresh(self):
|
||||
PathLog.track()
|
||||
self.form.blockSignals(True)
|
||||
self.updateTool()
|
||||
self.updateUI()
|
||||
self.form.blockSignals(False)
|
||||
|
||||
def selectShape(self):
|
||||
path = self.tool.BitShape
|
||||
if not path:
|
||||
path = PathPreferences.lastPathToolShape()
|
||||
foo = QtGui.QFileDialog.getOpenFileName(self.form,
|
||||
"Path - Tool Shape",
|
||||
path,
|
||||
"*.fcstd")
|
||||
if foo and foo[0]:
|
||||
PathPreferences.setLastPathToolShape(os.path.dirname(foo[0]))
|
||||
self.form.shapePath.setText(foo[0])
|
||||
self.updateShape()
|
||||
|
||||
def setupUI(self):
|
||||
PathLog.track()
|
||||
self.updateUI()
|
||||
|
||||
self.form.toolName.editingFinished.connect(self.refresh)
|
||||
self.form.shapePath.editingFinished.connect(self.updateShape)
|
||||
self.form.shapeSet.clicked.connect(self.selectShape)
|
||||
298
src/Mod/Path/PathScripts/PathToolBitGui.py
Normal file
298
src/Mod/Path/PathScripts/PathToolBitGui.py
Normal file
@@ -0,0 +1,298 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2019 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * 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.PathIconViewProvider as PathIconViewProvider
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathToolBit as PathToolBit
|
||||
import PathScripts.PathToolBitEdit as PathToolBitEdit
|
||||
import os
|
||||
|
||||
from PySide import QtCore, QtGui
|
||||
|
||||
__title__ = "Tool Bit UI"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
__doc__ = "Task panel editor for a ToolBit"
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
#PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
class ViewProvider(object):
|
||||
'''ViewProvider for a ToolBit.
|
||||
It's sole job is to provide an icon and invoke the TaskPanel on edit.'''
|
||||
|
||||
def __init__(self, vobj, name):
|
||||
PathLog.track(name, vobj.Object)
|
||||
self.panel = None
|
||||
self.icon = name
|
||||
self.obj = vobj.Object
|
||||
self.vobj = vobj
|
||||
vobj.Proxy = self
|
||||
|
||||
def attach(self, vobj):
|
||||
PathLog.track(vobj.Object)
|
||||
self.vobj = vobj
|
||||
self.obj = vobj.Object
|
||||
|
||||
def getIcon(self):
|
||||
png = self.obj.Proxy.getBitThumbnail(self.obj)
|
||||
if png:
|
||||
pixmap = QtGui.QPixmap()
|
||||
pixmap.loadFromData(png, 'PNG')
|
||||
return QtGui.QIcon(pixmap)
|
||||
return ':/icons/Path-ToolBit.svg'
|
||||
|
||||
def __getstate__(self):
|
||||
return None
|
||||
|
||||
def __setstate__(self, state):
|
||||
# pylint: disable=unused-argument
|
||||
return None
|
||||
|
||||
def onDelete(self, vobj, arg2=None):
|
||||
PathLog.track(vobj.Object.Label)
|
||||
vobj.Object.Proxy.onDelete(vobj.Object)
|
||||
|
||||
def getDisplayMode(self, mode):
|
||||
# pylint: disable=unused-argument
|
||||
return 'Default'
|
||||
|
||||
def _openTaskPanel(self, vobj, deleteOnReject):
|
||||
PathLog.track()
|
||||
self.panel = TaskPanel(vobj, deleteOnReject)
|
||||
FreeCADGui.Control.closeDialog()
|
||||
FreeCADGui.Control.showDialog(self.panel)
|
||||
self.panel.setupUi()
|
||||
|
||||
def setCreate(self, vobj):
|
||||
PathLog.track()
|
||||
self._openTaskPanel(vobj, True)
|
||||
|
||||
def setEdit(self, vobj, mode=0):
|
||||
# pylint: disable=unused-argument
|
||||
self._openTaskPanel(vobj, False)
|
||||
return True
|
||||
|
||||
def unsetEdit(self, vobj, mode):
|
||||
# pylint: disable=unused-argument
|
||||
FreeCADGui.Control.closeDialog()
|
||||
self.panel = None
|
||||
return
|
||||
|
||||
def claimChildren(self):
|
||||
if self.obj.BitBody:
|
||||
return [self.obj.BitBody]
|
||||
return []
|
||||
|
||||
def doubleClicked(self, vobj):
|
||||
self.setEdit(vobj)
|
||||
|
||||
class TaskPanel:
|
||||
'''TaskPanel for the SetupSheet - if it is being edited directly.'''
|
||||
|
||||
def __init__(self, vobj, deleteOnReject):
|
||||
PathLog.track(vobj.Object.Label)
|
||||
self.vobj = vobj
|
||||
self.obj = vobj.Object
|
||||
self.editor = PathToolBitEdit.ToolBitEditor(self.obj)
|
||||
self.form = self.editor.form
|
||||
self.deleteOnReject = deleteOnReject
|
||||
FreeCAD.ActiveDocument.openTransaction(translate('PathToolBit', 'Edit ToolBit'))
|
||||
|
||||
def reject(self):
|
||||
FreeCAD.ActiveDocument.abortTransaction()
|
||||
self.editor.reject()
|
||||
FreeCADGui.Control.closeDialog()
|
||||
if self.deleteOnReject:
|
||||
FreeCAD.ActiveDocument.openTransaction(translate('PathToolBit', 'Uncreate ToolBit'))
|
||||
self.editor.reject()
|
||||
FreeCAD.ActiveDocument.removeObject(self.obj.Name)
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def accept(self):
|
||||
self.editor.accept()
|
||||
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
FreeCADGui.ActiveDocument.resetEdit()
|
||||
FreeCADGui.Control.closeDialog()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def updateUI(self):
|
||||
self.editor.updateUI()
|
||||
|
||||
def updateModel(self):
|
||||
self.editor.updateTool()
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def setupUi(self):
|
||||
self.editor.setupUI()
|
||||
|
||||
|
||||
class ToolBitSelector(object):
|
||||
ToolRole = QtCore.Qt.UserRole + 1
|
||||
|
||||
def __init__(self):
|
||||
self.buttons = None
|
||||
self.editor = None
|
||||
self.dialog = None
|
||||
self.form = FreeCADGui.PySideUic.loadUi(':/panels/ToolBitSelector.ui')
|
||||
self.setupUI()
|
||||
|
||||
def updateTools(self, selected=None):
|
||||
PathLog.track()
|
||||
selItem = None
|
||||
self.form.tools.setUpdatesEnabled(False)
|
||||
if selected is None and self.form.tools.currentItem():
|
||||
selected = self.form.tools.currentItem().text()
|
||||
self.form.tools.clear()
|
||||
for tool in sorted(self.loadedTools(), key=lambda t: t.Label):
|
||||
icon = None
|
||||
if tool.ViewObject and tool.ViewObject.Proxy:
|
||||
icon = tool.ViewObject.Proxy.getIcon()
|
||||
if icon and isinstance(icon, QtGui.QIcon):
|
||||
item = QtGui.QListWidgetItem(icon, tool.Label)
|
||||
else:
|
||||
item = QtGui.QListWidgetItem(tool.Label)
|
||||
item.setData(self.ToolRole, tool)
|
||||
if selected == tool.Label:
|
||||
selItem = item
|
||||
self.form.tools.addItem(item)
|
||||
if selItem:
|
||||
self.form.tools.setCurrentItem(selItem)
|
||||
self.updateSelection()
|
||||
self.form.tools.setUpdatesEnabled(True)
|
||||
|
||||
def getTool(self):
|
||||
PathLog.track()
|
||||
self.updateTools()
|
||||
res = self.form.exec_()
|
||||
if 1 == res and self.form.tools.currentItem():
|
||||
return self.form.tools.currentItem().data(self.ToolRole)
|
||||
return None
|
||||
|
||||
def loadedTools(self):
|
||||
PathLog.track()
|
||||
if FreeCAD.ActiveDocument:
|
||||
return [o for o in FreeCAD.ActiveDocument.Objects if hasattr(o, 'Proxy') and isinstance(o.Proxy, PathToolBit.ToolBit)]
|
||||
return []
|
||||
|
||||
def loadTool(self):
|
||||
PathLog.track()
|
||||
tool = LoadTool(self.form)
|
||||
if tool:
|
||||
self.updateTools(tool.Label)
|
||||
|
||||
def createTool(self):
|
||||
PathLog.track()
|
||||
tool = PathToolBit.Factory.Create()
|
||||
|
||||
def accept():
|
||||
self.editor.accept()
|
||||
self.dialog.done(1)
|
||||
self.updateTools(tool.Label)
|
||||
|
||||
def reject():
|
||||
FreeCAD.ActiveDocument.openTransaction(translate('PathToolBit', 'Uncreate ToolBit'))
|
||||
self.editor.reject()
|
||||
self.dialog.done(0)
|
||||
FreeCAD.ActiveDocument.removeObject(tool.Name)
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
|
||||
self.dialog = QtGui.QDialog(self.form)
|
||||
layout = QtGui.QVBoxLayout(self.dialog)
|
||||
self.editor = PathToolBitEdit.ToolBitEditor(tool, self.dialog)
|
||||
self.editor.setupUI()
|
||||
self.buttons = QtGui.QDialogButtonBox(
|
||||
QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel,
|
||||
QtCore.Qt.Horizontal, self.dialog)
|
||||
layout.addWidget(self.buttons)
|
||||
self.buttons.accepted.connect(accept)
|
||||
self.buttons.rejected.connect(reject)
|
||||
print(self.dialog.exec_())
|
||||
|
||||
def updateSelection(self):
|
||||
PathLog.track()
|
||||
if self.form.tools.selectedItems():
|
||||
self.form.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(True)
|
||||
else:
|
||||
self.form.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(False)
|
||||
|
||||
def setupUI(self):
|
||||
PathLog.track()
|
||||
self.form.toolCreate.clicked.connect(self.createTool)
|
||||
self.form.toolLoad.clicked.connect(self.loadTool)
|
||||
self.form.tools.itemSelectionChanged.connect(self.updateSelection)
|
||||
self.form.tools.doubleClicked.connect(self.form.accept)
|
||||
|
||||
class ToolBitGuiFactory(PathToolBit.ToolBitFactory):
|
||||
|
||||
def Create(self, name='ToolBit', shapeFile=None):
|
||||
'''Create(name = 'ToolBit') ... creates a new tool bit.
|
||||
It is assumed the tool will be edited immediately so the internal bit body is still attached.'''
|
||||
FreeCAD.ActiveDocument.openTransaction(translate('PathToolBit', 'Create ToolBit'))
|
||||
tool = PathToolBit.ToolBitFactory.Create(self, name, shapeFile)
|
||||
PathIconViewProvider.Attach(tool.ViewObject, name)
|
||||
FreeCAD.ActiveDocument.commitTransaction()
|
||||
return tool
|
||||
|
||||
def GetToolFile(parent = None):
|
||||
if parent is None:
|
||||
parent = QtGui.QApplication.activeWindow()
|
||||
foo = QtGui.QFileDialog.getOpenFileName(parent, 'Tool', PathPreferences.lastPathToolBit(), '*.fctb')
|
||||
if foo and foo[0]:
|
||||
PathPreferences.setLastPathToolBit(os.path.dirname(foo[0]))
|
||||
return foo[0]
|
||||
return None
|
||||
|
||||
def GetToolFiles(parent = None):
|
||||
if parent is None:
|
||||
parent = QtGui.QApplication.activeWindow()
|
||||
foo = QtGui.QFileDialog.getOpenFileNames(parent, 'Tool', PathPreferences.lastPathToolBit(), '*.fctb')
|
||||
if foo and foo[0]:
|
||||
PathPreferences.setLastPathToolBit(os.path.dirname(foo[0][0]))
|
||||
return foo[0]
|
||||
return []
|
||||
|
||||
|
||||
def LoadTool(parent = None):
|
||||
'''LoadTool(parent=None) ... Open a file dialog to load a tool from a file.'''
|
||||
foo = GetToolFile(parent)
|
||||
return PathToolBit.Factory.CreateFrom(foo) if foo else foo
|
||||
|
||||
def LoadTools(parent = None):
|
||||
'''LoadTool(parent=None) ... Open a file dialog to load a tool from a file.'''
|
||||
return [PathToolBit.Factory.CreateFrom(foo) for foo in GetToolFiles(parent)]
|
||||
|
||||
# Set the factory so all tools are created with UI
|
||||
PathToolBit.Factory = ToolBitGuiFactory()
|
||||
|
||||
PathIconViewProvider.RegisterViewProvider('ToolBit', ViewProvider)
|
||||
100
src/Mod/Path/PathScripts/PathToolBitLibraryCmd.py
Normal file
100
src/Mod/Path/PathScripts/PathToolBitLibraryCmd.py
Normal file
@@ -0,0 +1,100 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2019 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * 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 PySide.QtCore as QtCore
|
||||
|
||||
class CommandToolBitLibraryOpen:
|
||||
'''
|
||||
Command to ToolBitLibrary editor.
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path-ToolTable',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("PathToolBitLibrary", "Open ToolBit Library editor"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathToolBitLibrary", "Open an editor to manage ToolBit libraries")}
|
||||
|
||||
def IsActive(self):
|
||||
return True
|
||||
|
||||
def Activated(self):
|
||||
import PathScripts.PathToolBitLibraryGui as PathToolBitLibraryGui
|
||||
library = PathToolBitLibraryGui.ToolBitLibrary()
|
||||
library.open()
|
||||
|
||||
class CommandToolBitLibraryLoad:
|
||||
'''
|
||||
Command used to load an entire ToolBitLibrary (or part of it) from a file into a job.
|
||||
'''
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap': 'Path-ToolTable',
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("PathToolBitLibrary", "Load ToolBit Library"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("PathToolBitLibrary", "Load an entire ToolBit library or part of it into a job")}
|
||||
|
||||
def selectedJob(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
sel = FreeCADGui.Selection.getSelectionEx()
|
||||
if sel and sel[0].Object.Name[:3] == 'Job':
|
||||
return sel[0].Object
|
||||
jobs = [o for o in FreeCAD.ActiveDocument.Objects if o.Name[:3] == 'Job']
|
||||
if 1 == len(jobs):
|
||||
return jobs[0]
|
||||
return None
|
||||
|
||||
def IsActive(self):
|
||||
return not self.selectedJob() is None
|
||||
|
||||
def Activated(self):
|
||||
job = self.selectedJob()
|
||||
self.Execute(job)
|
||||
|
||||
@classmethod
|
||||
def Execute(cls, job):
|
||||
import PathScripts.PathToolBitLibraryGui as PathToolBitLibraryGui
|
||||
import PathScripts.PathToolControllerGui as PathToolControllerGui
|
||||
|
||||
library = PathToolBitLibraryGui.ToolBitLibrary()
|
||||
if 1 == library.open(dialog=True) and job:
|
||||
for nr, tool in library.selectedOrAllTools():
|
||||
tc = PathToolControllerGui.Create("TC: {}".format(tool.Label), tool, nr)
|
||||
job.Proxy.addToolController(tc)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
return True
|
||||
return False
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
FreeCADGui.addCommand('Path_ToolBitLibraryOpen', CommandToolBitLibraryOpen())
|
||||
FreeCADGui.addCommand('Path_ToolBitLibraryLoad', CommandToolBitLibraryLoad())
|
||||
|
||||
CommandList = ['Path_ToolBitLibraryOpen', 'Path_ToolBitLibraryLoad']
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathToolBitLibraryCmd... done\n")
|
||||
306
src/Mod/Path/PathScripts/PathToolBitLibraryGui.py
Normal file
306
src/Mod/Path/PathScripts/PathToolBitLibraryGui.py
Normal file
@@ -0,0 +1,306 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2019 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * 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 FreeCADGui
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathToolBit as PathToolBit
|
||||
import PathScripts.PathToolBitGui as PathToolBitGui
|
||||
import PySide
|
||||
import json
|
||||
import os
|
||||
import traceback
|
||||
import uuid as UUID
|
||||
|
||||
#PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
_UuidRole = PySide.QtCore.Qt.UserRole + 1
|
||||
_PathRole = PySide.QtCore.Qt.UserRole + 2
|
||||
|
||||
class _TableView(PySide.QtGui.QTableView):
|
||||
'''Subclass of QTableView to support rearrange and copying of ToolBits'''
|
||||
|
||||
def __init__(self, parent):
|
||||
PySide.QtGui.QTableView.__init__(self, parent)
|
||||
self.setDragEnabled(True)
|
||||
self.setAcceptDrops(True)
|
||||
self.setDropIndicatorShown(True)
|
||||
self.setDragDropMode(PySide.QtGui.QAbstractItemView.InternalMove)
|
||||
self.setDefaultDropAction(PySide.QtCore.Qt.MoveAction)
|
||||
self.setSortingEnabled(True)
|
||||
self.setSelectionBehavior(PySide.QtGui.QAbstractItemView.SelectRows)
|
||||
self.verticalHeader().hide()
|
||||
|
||||
def supportedDropActions(self):
|
||||
return [PySide.QtCore.Qt.CopyAction, PySide.QtCore.Qt.MoveAction]
|
||||
|
||||
def _uuidOfRow(self, row):
|
||||
model = self.model()
|
||||
return model.data(model.index(row, 0), _UuidRole)
|
||||
|
||||
def _rowWithUuid(self, uuid):
|
||||
model = self.model()
|
||||
for row in range(model.rowCount()):
|
||||
if self._uuidOfRow(row) == uuid:
|
||||
return row
|
||||
return None
|
||||
|
||||
def _copyTool(self, uuid_, dstRow):
|
||||
model = self.model()
|
||||
model.insertRow(dstRow)
|
||||
srcRow = self._rowWithUuid(uuid_)
|
||||
for col in range(model.columnCount()):
|
||||
srcItem = model.item(srcRow, col)
|
||||
|
||||
model.setData(model.index(dstRow, col), srcItem.data(PySide.QtCore.Qt.EditRole), PySide.QtCore.Qt.EditRole)
|
||||
if col == 0:
|
||||
model.setData(model.index(dstRow, col), srcItem.data(_PathRole), _PathRole)
|
||||
# Even a clone of a tool gets its own uuid so it can be identified when
|
||||
# rearranging the order or inserting/deleting rows
|
||||
model.setData(model.index(dstRow, col), UUID.uuid4(), _UuidRole)
|
||||
else:
|
||||
model.item(dstRow, col).setEditable(False)
|
||||
|
||||
def _copyTools(self, uuids, dst):
|
||||
for i, uuid in enumerate(uuids):
|
||||
self._copyTool(uuid, dst + i)
|
||||
|
||||
def dropEvent(self, event):
|
||||
PathLog.track()
|
||||
mime = event.mimeData()
|
||||
data = mime.data('application/x-qstandarditemmodeldatalist')
|
||||
stream = PySide.QtCore.QDataStream(data)
|
||||
srcRows = []
|
||||
while not stream.atEnd():
|
||||
# pylint: disable=unused-variable
|
||||
row = stream.readInt32()
|
||||
srcRows.append(row)
|
||||
col = stream.readInt32()
|
||||
#PathLog.track(row, col)
|
||||
cnt = stream.readInt32()
|
||||
for i in range(cnt):
|
||||
key = stream.readInt32()
|
||||
val = stream.readQVariant()
|
||||
#PathLog.track(' ', i, key, val, type(val))
|
||||
# I have no idea what these three integers are,
|
||||
# or if they even are three integers,
|
||||
# but it seems to work out this way.
|
||||
i0 = stream.readInt32()
|
||||
i1 = stream.readInt32()
|
||||
i2 = stream.readInt32()
|
||||
#PathLog.track(' ', i0, i1, i2)
|
||||
|
||||
# get the uuids of all srcRows
|
||||
model = self.model()
|
||||
srcUuids = [self._uuidOfRow(row) for row in set(srcRows)]
|
||||
destRow = self.rowAt(event.pos().y())
|
||||
|
||||
self._copyTools(srcUuids, destRow)
|
||||
if PySide.QtCore.Qt.DropAction.MoveAction == event.proposedAction():
|
||||
for uuid in srcUuids:
|
||||
model.removeRow(self._rowWithUuid(uuid))
|
||||
|
||||
class ToolBitLibrary(object):
|
||||
'''ToolBitLibrary is the controller for displaying/selecting/creating/editing a collection of ToolBits.'''
|
||||
|
||||
def __init__(self, path=None):
|
||||
self.path = path
|
||||
self.form = FreeCADGui.PySideUic.loadUi(':/panels/ToolBitLibraryEdit.ui')
|
||||
self.toolTableView = _TableView(self.form.toolTableGroup)
|
||||
self.form.toolTableGroup.layout().replaceWidget(self.form.toolTable, self.toolTableView)
|
||||
self.form.toolTable.hide()
|
||||
self.setupUI()
|
||||
self.title = self.form.windowTitle()
|
||||
if path:
|
||||
self.libraryLoad(path)
|
||||
|
||||
def _toolAdd(self, nr, tool, path):
|
||||
toolNr = PySide.QtGui.QStandardItem()
|
||||
toolNr.setData(nr, PySide.QtCore.Qt.EditRole)
|
||||
toolNr.setData(path, _PathRole)
|
||||
toolNr.setData(UUID.uuid4(), _UuidRole)
|
||||
|
||||
toolName = PySide.QtGui.QStandardItem()
|
||||
toolName.setData(tool['name'], PySide.QtCore.Qt.EditRole)
|
||||
toolName.setEditable(False)
|
||||
|
||||
toolShape = PySide.QtGui.QStandardItem()
|
||||
toolShape.setData(os.path.splitext(os.path.basename(tool['shape']))[0], PySide.QtCore.Qt.EditRole)
|
||||
toolShape.setEditable(False)
|
||||
|
||||
toolDiameter = PySide.QtGui.QStandardItem()
|
||||
toolDiameter.setData(tool['parameter']['Diameter'], PySide.QtCore.Qt.EditRole)
|
||||
toolDiameter.setEditable(False)
|
||||
|
||||
self.model.appendRow([toolNr, toolName, toolShape, toolDiameter])
|
||||
|
||||
def toolAdd(self):
|
||||
PathLog.track()
|
||||
# pylint: disable=broad-except
|
||||
try:
|
||||
nr = 0
|
||||
for row in range(self.model.rowCount()):
|
||||
itemNr = int(self.model.item(row, 0).data(PySide.QtCore.Qt.EditRole))
|
||||
nr = max(nr, itemNr)
|
||||
nr += 1
|
||||
|
||||
for i, foo in enumerate(PathToolBitGui.GetToolFiles(self.form)):
|
||||
tool = PathToolBit.Declaration(foo)
|
||||
self._toolAdd(nr + i, tool, foo)
|
||||
self.toolTableView.resizeColumnsToContents()
|
||||
except Exception:
|
||||
PathLog.error('something happened')
|
||||
PathLog.error(traceback.print_exc())
|
||||
|
||||
def selectedOrAllTools(self):
|
||||
selectedRows = set([index.row() for index in self.toolTableView.selectedIndexes()])
|
||||
if not selectedRows:
|
||||
selectedRows = list(range(self.model.rowCount()))
|
||||
tools = []
|
||||
for row in selectedRows:
|
||||
item = self.model.item(row, 0)
|
||||
toolNr = int(item.data(PySide.QtCore.Qt.EditRole))
|
||||
toolPath = item.data(_PathRole)
|
||||
tools.append((toolNr, PathToolBit.Factory.CreateFrom(toolPath)))
|
||||
return tools
|
||||
|
||||
def toolDelete(self):
|
||||
PathLog.track()
|
||||
selectedRows = set([index.row() for index in self.toolTableView.selectedIndexes()])
|
||||
for row in sorted(list(selectedRows), key = lambda r: -r):
|
||||
self.model.removeRows(row, 1)
|
||||
|
||||
def toolEnumerate(self):
|
||||
PathLog.track()
|
||||
for row in range(self.model.rowCount()):
|
||||
self.model.setData(self.model.index(row, 0), row + 1, PySide.QtCore.Qt.EditRole)
|
||||
|
||||
def toolSelect(self, selected, deselected):
|
||||
# pylint: disable=unused-argument
|
||||
self.form.toolDelete.setEnabled(len(self.toolTableView.selectedIndexes()) > 0)
|
||||
|
||||
def open(self, path=None, dialog=False):
|
||||
'''open(path=None, dialog=False) ... load library stored in path and bring up ui.
|
||||
Returns 1 if user pressed OK, 0 otherwise.'''
|
||||
if path:
|
||||
fullPath = PathToolBit.findLibrary(path)
|
||||
if fullPath:
|
||||
self.libraryLoad(fullPath)
|
||||
else:
|
||||
self.libraryOpen()
|
||||
elif dialog:
|
||||
self.libraryOpen()
|
||||
return self.form.exec_()
|
||||
|
||||
def updateToolbar(self):
|
||||
if self.path:
|
||||
self.form.librarySave.setEnabled(True)
|
||||
else:
|
||||
self.form.librarySave.setEnabled(False)
|
||||
|
||||
def libraryOpen(self):
|
||||
PathLog.track()
|
||||
foo = PySide.QtGui.QFileDialog.getOpenFileName(self.form, 'Tool Library', PathPreferences.lastPathToolLibrary(), '*.fctl')
|
||||
if foo and foo[0]:
|
||||
path = foo[0]
|
||||
PathPreferences.setLastPathToolLibrary(os.path.dirname(path))
|
||||
self.libraryLoad(path)
|
||||
|
||||
def libraryLoad(self, path):
|
||||
self.toolTableView.setUpdatesEnabled(False)
|
||||
self.model.clear()
|
||||
self.model.setHorizontalHeaderLabels(self.columnNames())
|
||||
if path:
|
||||
with open(path) as fp:
|
||||
library = json.load(fp)
|
||||
for toolBit in library['tools']:
|
||||
nr = toolBit['nr']
|
||||
bit = PathToolBit.findBit(toolBit['path'])
|
||||
if bit:
|
||||
PathLog.track(bit)
|
||||
tool = PathToolBit.Declaration(bit)
|
||||
self._toolAdd(nr, tool, bit)
|
||||
else:
|
||||
PathLog.error("Could not find tool #{}: {}".format(nr, library['tools'][nr]))
|
||||
self.toolTableView.resizeColumnsToContents()
|
||||
self.toolTableView.setUpdatesEnabled(True)
|
||||
|
||||
self.form.setWindowTitle("{} - {}".format(self.title, os.path.basename(path) if path else ''))
|
||||
self.path = path
|
||||
self.updateToolbar()
|
||||
|
||||
def libraryNew(self):
|
||||
self.libraryLoad(None)
|
||||
|
||||
def librarySave(self):
|
||||
library = {}
|
||||
tools = []
|
||||
library['version'] = 1
|
||||
library['tools'] = tools
|
||||
for row in range(self.model.rowCount()):
|
||||
toolNr = self.model.data(self.model.index(row, 0), PySide.QtCore.Qt.EditRole)
|
||||
toolPath = self.model.data(self.model.index(row, 0), _PathRole)
|
||||
if PathPreferences.toolsStoreAbsolutePaths():
|
||||
tools.append({'nr': toolNr, 'path': toolPath})
|
||||
else:
|
||||
tools.append({'nr': toolNr, 'path': PathToolBit.findRelativePathTool(toolPath)})
|
||||
|
||||
with open(self.path, 'w') as fp:
|
||||
json.dump(library, fp, sort_keys=True, indent=2)
|
||||
|
||||
def librarySaveAs(self):
|
||||
foo = PySide.QtGui.QFileDialog.getSaveFileName(self.form, 'Tool Library', PathPreferences.lastPathToolLibrary(), '*.fctl')
|
||||
if foo and foo[0]:
|
||||
path = foo[0] if foo[0].endswith('.fctl') else "{}.fctl".format(foo[0])
|
||||
PathPreferences.setLastPathToolLibrary(os.path.dirname(path))
|
||||
self.path = path
|
||||
self.librarySave()
|
||||
self.updateToolbar()
|
||||
|
||||
def columnNames(self):
|
||||
return ['Nr', 'Tool', 'Shape', 'Diameter']
|
||||
|
||||
def setupUI(self):
|
||||
PathLog.track('+')
|
||||
self.model = PySide.QtGui.QStandardItemModel(0, len(self.columnNames()), self.toolTableView)
|
||||
self.model.setHorizontalHeaderLabels(self.columnNames())
|
||||
|
||||
self.toolTableView.setModel(self.model)
|
||||
self.toolTableView.resizeColumnsToContents()
|
||||
self.toolTableView.selectionModel().selectionChanged.connect(self.toolSelect)
|
||||
|
||||
self.form.toolAdd.clicked.connect(self.toolAdd)
|
||||
self.form.toolDelete.clicked.connect(self.toolDelete)
|
||||
self.form.toolEnumerate.clicked.connect(self.toolEnumerate)
|
||||
|
||||
self.form.libraryNew.clicked.connect(self.libraryNew)
|
||||
self.form.libraryOpen.clicked.connect(self.libraryOpen)
|
||||
self.form.librarySave.clicked.connect(self.librarySave)
|
||||
self.form.librarySaveAs.clicked.connect(self.librarySaveAs)
|
||||
|
||||
self.toolSelect([], [])
|
||||
self.updateToolbar()
|
||||
PathLog.track('-')
|
||||
@@ -26,16 +26,13 @@
|
||||
import FreeCAD
|
||||
import Path
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathToolBit as PathToolBit
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
LOGLEVEL = False
|
||||
|
||||
if LOGLEVEL:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
@@ -61,54 +58,74 @@ class ToolControllerTemplate:
|
||||
VertRapid = 'vrapid'
|
||||
|
||||
class ToolController:
|
||||
def __init__(self, obj, tool=1):
|
||||
PathLog.track('tool: {}'.format(tool))
|
||||
|
||||
obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", QtCore.QT_TRANSLATE_NOOP("App::Property", "The active tool"))
|
||||
def __init__(self, obj, cTool=False):
|
||||
PathLog.track('tool: {}'.format(cTool))
|
||||
|
||||
obj.addProperty("App::PropertyIntegerConstraint", "ToolNumber", "Tool", QtCore.QT_TRANSLATE_NOOP("PathToolController", "The active tool"))
|
||||
obj.ToolNumber = (0, 0, 10000, 1)
|
||||
obj.addProperty("Path::PropertyTool", "Tool", "Base", QtCore.QT_TRANSLATE_NOOP("App::Property", "The tool used by this controller"))
|
||||
|
||||
obj.addProperty("App::PropertyFloat", "SpindleSpeed", "Tool", QtCore.QT_TRANSLATE_NOOP("App::Property", "The speed of the cutting spindle in RPM"))
|
||||
obj.addProperty("App::PropertyEnumeration", "SpindleDir", "Tool", QtCore.QT_TRANSLATE_NOOP("App::Property", "Direction of spindle rotation"))
|
||||
self.ensureUseLegacyTool(obj, cTool)
|
||||
obj.addProperty("App::PropertyFloat", "SpindleSpeed", "Tool", QtCore.QT_TRANSLATE_NOOP("PathToolController", "The speed of the cutting spindle in RPM"))
|
||||
obj.addProperty("App::PropertyEnumeration", "SpindleDir", "Tool", QtCore.QT_TRANSLATE_NOOP("PathToolController", "Direction of spindle rotation"))
|
||||
obj.SpindleDir = ['Forward', 'Reverse']
|
||||
obj.addProperty("App::PropertySpeed", "VertFeed", "Feed", QtCore.QT_TRANSLATE_NOOP("App::Property", "Feed rate for vertical moves in Z"))
|
||||
obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed", QtCore.QT_TRANSLATE_NOOP("App::Property", "Feed rate for horizontal moves"))
|
||||
obj.addProperty("App::PropertySpeed", "VertRapid", "Rapid", QtCore.QT_TRANSLATE_NOOP("App::Property", "Rapid rate for vertical moves in Z"))
|
||||
obj.addProperty("App::PropertySpeed", "HorizRapid", "Rapid", QtCore.QT_TRANSLATE_NOOP("App::Property", "Rapid rate for horizontal moves"))
|
||||
obj.Proxy = self
|
||||
obj.addProperty("App::PropertySpeed", "VertFeed", "Feed", QtCore.QT_TRANSLATE_NOOP("PathToolController", "Feed rate for vertical moves in Z"))
|
||||
obj.addProperty("App::PropertySpeed", "HorizFeed", "Feed", QtCore.QT_TRANSLATE_NOOP("PathToolController", "Feed rate for horizontal moves"))
|
||||
obj.addProperty("App::PropertySpeed", "VertRapid", "Rapid", QtCore.QT_TRANSLATE_NOOP("PathToolController", "Rapid rate for vertical moves in Z"))
|
||||
obj.addProperty("App::PropertySpeed", "HorizRapid", "Rapid", QtCore.QT_TRANSLATE_NOOP("PathToolController", "Rapid rate for horizontal moves"))
|
||||
obj.setEditorMode('Placement', 2)
|
||||
|
||||
def onDocumentRestored(self, obj):
|
||||
obj.setEditorMode('Placement', 2)
|
||||
|
||||
def onDelete(self, obj, arg2=None):
|
||||
# pylint: disable=unused-argument
|
||||
if not self.usesLegacyTool(obj):
|
||||
if len(obj.Tool.InList) == 1:
|
||||
if hasattr(obj.Tool.Proxy, 'onDelete'):
|
||||
obj.Tool.Proxy.onDelete(obj.Tool)
|
||||
obj.Document.removeObject(obj.Tool.Name)
|
||||
|
||||
def setFromTemplate(self, obj, template):
|
||||
'''setFromTemplate(obj, xmlItem) ... extract properties from xmlItem and assign to receiver.'''
|
||||
PathLog.track(obj.Name, template)
|
||||
if template.get(ToolControllerTemplate.Version) and 1 == int(template.get(ToolControllerTemplate.Version)):
|
||||
if template.get(ToolControllerTemplate.Label):
|
||||
obj.Label = template.get(ToolControllerTemplate.Label)
|
||||
if template.get(ToolControllerTemplate.VertFeed):
|
||||
obj.VertFeed = template.get(ToolControllerTemplate.VertFeed)
|
||||
if template.get(ToolControllerTemplate.HorizFeed):
|
||||
obj.HorizFeed = template.get(ToolControllerTemplate.HorizFeed)
|
||||
if template.get(ToolControllerTemplate.VertRapid):
|
||||
obj.VertRapid = template.get(ToolControllerTemplate.VertRapid)
|
||||
if template.get(ToolControllerTemplate.HorizRapid):
|
||||
obj.HorizRapid = template.get(ToolControllerTemplate.HorizRapid)
|
||||
if template.get(ToolControllerTemplate.SpindleSpeed):
|
||||
obj.SpindleSpeed = float(template.get(ToolControllerTemplate.SpindleSpeed))
|
||||
if template.get(ToolControllerTemplate.SpindleDir):
|
||||
obj.SpindleDir = template.get(ToolControllerTemplate.SpindleDir)
|
||||
if template.get(ToolControllerTemplate.ToolNumber):
|
||||
obj.ToolNumber = int(template.get(ToolControllerTemplate.ToolNumber))
|
||||
if template.get(ToolControllerTemplate.Tool):
|
||||
obj.Tool.setFromTemplate(template.get(ToolControllerTemplate.Tool))
|
||||
if template.get(ToolControllerTemplate.Expressions):
|
||||
for exprDef in template.get(ToolControllerTemplate.Expressions):
|
||||
if exprDef[ToolControllerTemplate.ExprExpr]:
|
||||
obj.setExpression(exprDef[ToolControllerTemplate.ExprProp], exprDef[ToolControllerTemplate.ExprExpr])
|
||||
version = 0
|
||||
if template.get(ToolControllerTemplate.Version):
|
||||
version = int(template.get(ToolControllerTemplate.Version))
|
||||
if version == 1 or version == 2:
|
||||
if template.get(ToolControllerTemplate.Label):
|
||||
obj.Label = template.get(ToolControllerTemplate.Label)
|
||||
if template.get(ToolControllerTemplate.VertFeed):
|
||||
obj.VertFeed = template.get(ToolControllerTemplate.VertFeed)
|
||||
if template.get(ToolControllerTemplate.HorizFeed):
|
||||
obj.HorizFeed = template.get(ToolControllerTemplate.HorizFeed)
|
||||
if template.get(ToolControllerTemplate.VertRapid):
|
||||
obj.VertRapid = template.get(ToolControllerTemplate.VertRapid)
|
||||
if template.get(ToolControllerTemplate.HorizRapid):
|
||||
obj.HorizRapid = template.get(ToolControllerTemplate.HorizRapid)
|
||||
if template.get(ToolControllerTemplate.SpindleSpeed):
|
||||
obj.SpindleSpeed = float(template.get(ToolControllerTemplate.SpindleSpeed))
|
||||
if template.get(ToolControllerTemplate.SpindleDir):
|
||||
obj.SpindleDir = template.get(ToolControllerTemplate.SpindleDir)
|
||||
if template.get(ToolControllerTemplate.ToolNumber):
|
||||
obj.ToolNumber = int(template.get(ToolControllerTemplate.ToolNumber))
|
||||
if template.get(ToolControllerTemplate.Tool):
|
||||
toolVersion = template.get(ToolControllerTemplate.Tool).get(ToolControllerTemplate.Version)
|
||||
if toolVersion == 1:
|
||||
self.ensureUseLegacyTool(obj, True)
|
||||
obj.Tool.setFromTemplate(template.get(ToolControllerTemplate.Tool))
|
||||
else:
|
||||
self.ensureUseLegacyTool(obj, False)
|
||||
obj.Tool = PathToolBit.Factory.CreateFromAttrs(template.get(ToolControllerTemplate.Tool))
|
||||
if obj.Tool and obj.Tool.ViewObject and obj.Tool.ViewObject.Visibility:
|
||||
obj.Tool.ViewObject.Visibility = False
|
||||
if template.get(ToolControllerTemplate.Expressions):
|
||||
for exprDef in template.get(ToolControllerTemplate.Expressions):
|
||||
if exprDef[ToolControllerTemplate.ExprExpr]:
|
||||
obj.setExpression(exprDef[ToolControllerTemplate.ExprProp], exprDef[ToolControllerTemplate.ExprExpr])
|
||||
else:
|
||||
PathLog.error(translate('PathToolController', "Unsupported PathToolController template version %s") % template.get(ToolControllerTemplate.Version))
|
||||
else:
|
||||
PathLog.error(translate('PathToolController', "Unsupported PathToolController template version %s") % template.get(ToolControllerTemplate.Version))
|
||||
PathLog.error(translate('PathToolController', 'PathToolController template has no version - corrupted template file?'))
|
||||
|
||||
def templateAttrs(self, obj):
|
||||
'''templateAttrs(obj) ... answer a dictionary with all properties that should be stored for a template.'''
|
||||
@@ -123,7 +140,10 @@ class ToolController:
|
||||
attrs[ToolControllerTemplate.HorizRapid] = ("%s" % (obj.HorizRapid))
|
||||
attrs[ToolControllerTemplate.SpindleSpeed] = obj.SpindleSpeed
|
||||
attrs[ToolControllerTemplate.SpindleDir] = obj.SpindleDir
|
||||
attrs[ToolControllerTemplate.Tool] = obj.Tool.templateAttrs()
|
||||
if self.usesLegacyTool(obj):
|
||||
attrs[ToolControllerTemplate.Tool] = obj.Tool.templateAttrs()
|
||||
else:
|
||||
attrs[ToolControllerTemplate.Tool] = obj.Tool.Proxy.templateAttrs(obj.Tool)
|
||||
expressions = []
|
||||
for expr in obj.ExpressionEngine:
|
||||
PathLog.debug('%s: %s' % (expr[0], expr[1]))
|
||||
@@ -157,39 +177,61 @@ class ToolController:
|
||||
PathLog.track()
|
||||
return obj.Tool
|
||||
|
||||
def usesLegacyTool(self, obj):
|
||||
'''returns True if the tool being controlled is a legacy tool'''
|
||||
return isinstance(obj.Tool, Path.Tool)
|
||||
|
||||
def ensureUseLegacyTool(self, obj, legacy):
|
||||
if not hasattr(obj, 'Tool') or (legacy != self.usesLegacyTool(obj)):
|
||||
if legacy and hasattr(obj, 'Tool') and len(obj.Tool.InList) == 1:
|
||||
if hasattr(obj.Tool.Proxy, 'onDelete'):
|
||||
obj.Tool.Proxy.onDelete(obj.Tool)
|
||||
obj.Document.removeObject(obj.Tool.Name)
|
||||
|
||||
if hasattr(obj, 'Tool'):
|
||||
obj.removeProperty('Tool')
|
||||
|
||||
if legacy:
|
||||
obj.addProperty("Path::PropertyTool", "Tool", "Base", QtCore.QT_TRANSLATE_NOOP("PathToolController", "The tool used by this controller"))
|
||||
else:
|
||||
obj.addProperty("App::PropertyLink", "Tool", "Base", QtCore.QT_TRANSLATE_NOOP("PathToolController", "The tool used by this controller"))
|
||||
|
||||
def Create(name = 'Default Tool', tool=None, toolNumber=1, assignViewProvider=True):
|
||||
PathLog.track(tool, toolNumber)
|
||||
legacyTool = PathPreferences.toolsReallyUseLegacyTools() if tool is None else isinstance(tool, Path.Tool)
|
||||
|
||||
PathLog.track(tool, toolNumber, legacyTool)
|
||||
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Label = name
|
||||
obj.Proxy = ToolController(obj, legacyTool)
|
||||
|
||||
ToolController(obj)
|
||||
if FreeCAD.GuiUp and assignViewProvider:
|
||||
ViewProvider(obj.ViewObject)
|
||||
|
||||
if tool is None:
|
||||
tool = Path.Tool()
|
||||
tool.Diameter = 5.0
|
||||
tool.Name = "Default Tool"
|
||||
tool.CuttingEdgeHeight = 15.0
|
||||
tool.ToolType = "EndMill"
|
||||
tool.Material = "HighSpeedSteel"
|
||||
if legacyTool:
|
||||
tool = Path.Tool()
|
||||
tool.Diameter = 5.0
|
||||
tool.Name = "Default Tool"
|
||||
tool.CuttingEdgeHeight = 15.0
|
||||
tool.ToolType = "EndMill"
|
||||
tool.Material = "HighSpeedSteel"
|
||||
else:
|
||||
tool = PathToolBit.Factory.Create()
|
||||
if tool.ViewObject:
|
||||
tool.ViewObject.Visibility = False
|
||||
|
||||
obj.Tool = tool
|
||||
obj.ToolNumber = toolNumber
|
||||
return obj
|
||||
|
||||
def FromTemplate(template, assignViewProvider=True):
|
||||
# pylint: disable=unused-argument
|
||||
PathLog.track()
|
||||
|
||||
name = template.get(ToolControllerTemplate.Name, ToolControllerTemplate.Label)
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
tc = ToolController(obj)
|
||||
if FreeCAD.GuiUp and assignViewProvider:
|
||||
ViewProvider(obj.ViewObject)
|
||||
|
||||
tc.setFromTemplate(obj, template)
|
||||
obj = Create(name, assignViewProvider=True)
|
||||
obj.Proxy.setFromTemplate(obj, template)
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import Part
|
||||
import PathScripts
|
||||
import PathScripts.PathGui as PathGui
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathToolBitGui as PathToolBitGui
|
||||
import PathScripts.PathToolEdit as PathToolEdit
|
||||
import PathScripts.PathUtil as PathUtil
|
||||
|
||||
@@ -78,6 +79,7 @@ class ViewProvider:
|
||||
def onDelete(self, vobj, args=None):
|
||||
# pylint: disable=unused-argument
|
||||
PathUtil.clearExpressionEngine(vobj.Object)
|
||||
self.vobj.Object.Proxy.onDelete(vobj.Object, args)
|
||||
return True
|
||||
|
||||
def updateData(self, vobj, prop):
|
||||
@@ -113,11 +115,21 @@ class ViewProvider:
|
||||
action.triggered.connect(self.setEdit)
|
||||
menu.addAction(action)
|
||||
|
||||
def claimChildren(self):
|
||||
obj = self.vobj.Object
|
||||
if obj and obj.Proxy and not obj.Proxy.usesLegacyTool(obj):
|
||||
return [obj.Tool]
|
||||
return []
|
||||
|
||||
def Create(name = 'Default Tool', tool=None, toolNumber=1):
|
||||
PathLog.track(tool, toolNumber)
|
||||
|
||||
obj = PathScripts.PathToolController.Create(name, tool, toolNumber)
|
||||
ViewProvider(obj.ViewObject)
|
||||
if not obj.Proxy.usesLegacyTool(obj):
|
||||
# ToolBits are visible by default, which is typically not what the user wants
|
||||
if tool and tool.ViewObject and tool.ViewObject.Visibility:
|
||||
tool.ViewObject.Visibility = False
|
||||
return obj
|
||||
|
||||
|
||||
@@ -129,16 +141,35 @@ class CommandPathToolController(object):
|
||||
'MenuText': QtCore.QT_TRANSLATE_NOOP("Path_ToolController", "Add Tool Controller to the Job"),
|
||||
'ToolTip': QtCore.QT_TRANSLATE_NOOP("Path_ToolController", "Add Tool Controller")}
|
||||
|
||||
def selectedJob(self):
|
||||
if FreeCAD.ActiveDocument:
|
||||
sel = FreeCADGui.Selection.getSelectionEx()
|
||||
if sel and sel[0].Object.Name[:3] == 'Job':
|
||||
return sel[0].Object
|
||||
jobs = [o for o in FreeCAD.ActiveDocument.Objects if o.Name[:3] == 'Job']
|
||||
if 1 == len(jobs):
|
||||
return jobs[0]
|
||||
return None
|
||||
|
||||
def IsActive(self):
|
||||
if FreeCAD.ActiveDocument is not None:
|
||||
for o in FreeCAD.ActiveDocument.Objects:
|
||||
if o.Name[:3] == "Job":
|
||||
return True
|
||||
return False
|
||||
return self.selectedJob() is not None
|
||||
|
||||
def Activated(self):
|
||||
PathLog.track()
|
||||
Create()
|
||||
job = self.selectedJob()
|
||||
if job:
|
||||
tool = PathToolBitGui.ToolBitSelector().getTool()
|
||||
if tool:
|
||||
toolNr = None
|
||||
for tc in job.ToolController:
|
||||
if tc.Tool == tool:
|
||||
toolNr = tc.ToolNumber
|
||||
break
|
||||
if not toolNr:
|
||||
toolNr = max([tc.ToolNumber for tc in job.ToolController]) + 1
|
||||
tc = Create("TC: {}".format(tool.Label), tool, toolNr)
|
||||
job.Proxy.addToolController(tc)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
class ToolControllerEditor(object):
|
||||
|
||||
@@ -153,7 +184,12 @@ class ToolControllerEditor(object):
|
||||
self.vertRapid = PathGui.QuantitySpinBox(self.form.vertRapid, obj, 'VertRapid')
|
||||
self.horizRapid = PathGui.QuantitySpinBox(self.form.horizRapid, obj, 'HorizRapid')
|
||||
|
||||
self.editor = PathToolEdit.ToolEditor(obj.Tool, self.form.toolEditor)
|
||||
if obj.Proxy.usesLegacyTool(obj):
|
||||
self.editor = PathToolEdit.ToolEditor(obj.Tool, self.form.toolEditor)
|
||||
else:
|
||||
self.editor = None
|
||||
self.form.toolBox.widget(1).hide()
|
||||
self.form.toolBox.removeItem(1)
|
||||
|
||||
def updateUi(self):
|
||||
tc = self.obj
|
||||
@@ -168,7 +204,8 @@ class ToolControllerEditor(object):
|
||||
if index >= 0:
|
||||
self.form.spindleDirection.setCurrentIndex(index)
|
||||
|
||||
self.editor.updateUI()
|
||||
if self.editor:
|
||||
self.editor.updateUI()
|
||||
|
||||
def updateToolController(self):
|
||||
tc = self.obj
|
||||
@@ -182,8 +219,9 @@ class ToolControllerEditor(object):
|
||||
tc.SpindleSpeed = self.form.spindleSpeed.value()
|
||||
tc.SpindleDir = self.form.spindleDirection.currentText()
|
||||
|
||||
self.editor.updateTool()
|
||||
tc.Tool = self.editor.tool
|
||||
if self.editor:
|
||||
self.editor.updateTool()
|
||||
tc.Tool = self.editor.tool
|
||||
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
PathLog.error(translate("PathToolController", "Error updating TC: %s") % e)
|
||||
@@ -196,7 +234,8 @@ class ToolControllerEditor(object):
|
||||
self.form.blockSignals(False)
|
||||
|
||||
def setupUi(self):
|
||||
self.editor.setupUI()
|
||||
if self.editor:
|
||||
self.editor.setupUI()
|
||||
|
||||
self.form.tcName.editingFinished.connect(self.refresh)
|
||||
self.form.horizFeed.editingFinished.connect(self.refresh)
|
||||
@@ -219,13 +258,13 @@ class TaskPanel:
|
||||
|
||||
FreeCADGui.ActiveDocument.resetEdit()
|
||||
FreeCADGui.Control.closeDialog()
|
||||
if self.toolrep is not None:
|
||||
if self.toolrep:
|
||||
FreeCAD.ActiveDocument.removeObject(self.toolrep.Name)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def reject(self):
|
||||
FreeCADGui.Control.closeDialog()
|
||||
if self.toolrep is not None:
|
||||
if self.toolrep:
|
||||
FreeCAD.ActiveDocument.removeObject(self.toolrep.Name)
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
@@ -236,11 +275,12 @@ class TaskPanel:
|
||||
def setFields(self):
|
||||
self.editor.updateUi()
|
||||
|
||||
tool = self.obj.Tool
|
||||
radius = tool.Diameter / 2
|
||||
length = tool.CuttingEdgeHeight
|
||||
t = Part.makeCylinder(radius, length)
|
||||
self.toolrep.Shape = t
|
||||
if self.toolrep:
|
||||
tool = self.obj.Tool
|
||||
radius = float(tool.Diameter) / 2
|
||||
length = tool.CuttingEdgeHeight
|
||||
t = Part.makeCylinder(radius, length)
|
||||
self.toolrep.Shape = t
|
||||
|
||||
def edit(self, item, column):
|
||||
# pylint: disable=unused-argument
|
||||
@@ -253,9 +293,10 @@ class TaskPanel:
|
||||
FreeCAD.ActiveDocument.recompute()
|
||||
|
||||
def setupUi(self):
|
||||
t = Part.makeCylinder(1, 1)
|
||||
self.toolrep = FreeCAD.ActiveDocument.addObject("Part::Feature", "tool")
|
||||
self.toolrep.Shape = t
|
||||
if self.editor.editor:
|
||||
t = Part.makeCylinder(1, 1)
|
||||
self.toolrep = FreeCAD.ActiveDocument.addObject("Part::Feature", "tool")
|
||||
self.toolrep.Shape = t
|
||||
|
||||
self.setFields()
|
||||
self.editor.setupUi()
|
||||
|
||||
@@ -29,6 +29,8 @@ import FreeCADGui
|
||||
import Path
|
||||
import PathScripts
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathPreferences as PathPreferences
|
||||
import PathScripts.PathToolBitLibraryCmd as PathToolBitLibraryCmd
|
||||
import PathScripts.PathToolEdit as PathToolEdit
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import PathScripts.PathToolLibraryManager as ToolLibraryManager
|
||||
@@ -439,12 +441,14 @@ class CommandToolLibraryEdit():
|
||||
pass
|
||||
|
||||
def edit(self, job=None, cb=None):
|
||||
editor = EditorPanel(job, cb)
|
||||
editor.setupUi()
|
||||
|
||||
r = editor.form.exec_()
|
||||
if r:
|
||||
pass
|
||||
if PathPreferences.toolsReallyUseLegacyTools():
|
||||
editor = EditorPanel(job, cb)
|
||||
editor.setupUi()
|
||||
editor.form.exec_()
|
||||
else:
|
||||
if PathToolBitLibraryCmd.CommandToolBitLibraryLoad.Execute(job):
|
||||
if cb:
|
||||
cb()
|
||||
|
||||
def GetResources(self):
|
||||
return {'Pixmap' : 'Path-ToolTable',
|
||||
@@ -456,7 +460,6 @@ class CommandToolLibraryEdit():
|
||||
return not FreeCAD.ActiveDocument is None
|
||||
|
||||
def Activated(self):
|
||||
|
||||
self.edit()
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
|
||||
@@ -34,14 +34,47 @@ other than PathLog, then it probably doesn't belong here.
|
||||
|
||||
import six
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PySide
|
||||
|
||||
LOGLEVEL = False
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
if LOGLEVEL:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
def translate(context, text, disambig=None):
|
||||
return PySide.QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
|
||||
def _getProperty(obj, prop):
|
||||
o = obj
|
||||
attr = obj
|
||||
name = None
|
||||
for name in prop.split('.'):
|
||||
o = attr
|
||||
if not hasattr(o, name):
|
||||
break
|
||||
attr = getattr(o, name)
|
||||
|
||||
if o == attr:
|
||||
PathLog.warning(translate('PathGui', "%s has no property %s (%s))") % (obj.Label, prop, name))
|
||||
return (None, None, None)
|
||||
|
||||
#PathLog.debug("found property %s of %s (%s: %s)" % (prop, obj.Label, name, attr))
|
||||
return(o, attr, name)
|
||||
|
||||
def getProperty(obj, prop):
|
||||
'''getProperty(obj, prop) ... answer obj's property defined by its canonical name.'''
|
||||
o, attr, name = _getProperty(obj, prop) # pylint: disable=unused-variable
|
||||
return attr
|
||||
|
||||
def getPropertyValueString(obj, prop):
|
||||
'''getPropertyValueString(obj, prop) ... answer a string represntation of an object's property's value.'''
|
||||
attr = getProperty(obj, prop)
|
||||
if hasattr(attr, 'UserString'):
|
||||
return attr.UserString
|
||||
return str(attr)
|
||||
|
||||
def setProperty(obj, prop, value):
|
||||
'''setProperty(obj, prop, value) ... set the property value of obj's property defined by its canonical name.'''
|
||||
o, attr, name = _getProperty(obj, prop) # pylint: disable=unused-variable
|
||||
if o and name:
|
||||
setattr(o, name, value)
|
||||
|
||||
# NotValidBaseTypeIds = ['Sketcher::SketchObject']
|
||||
NotValidBaseTypeIds = []
|
||||
@@ -53,6 +86,9 @@ def isValidBaseObject(obj):
|
||||
# Can't link to anything inside a geo feature group anymore
|
||||
PathLog.debug("%s is inside a geo feature group" % obj.Label)
|
||||
return False
|
||||
if hasattr(obj, 'BitBody') and hasattr(obj, 'BitShape'):
|
||||
# ToolBit's are not valid base objects
|
||||
return False
|
||||
if obj.TypeId in NotValidBaseTypeIds:
|
||||
PathLog.debug("%s is blacklisted (%s)" % (obj.Label, obj.TypeId))
|
||||
return False
|
||||
|
||||
@@ -703,14 +703,14 @@ def guessDepths(objshape, subs=None):
|
||||
|
||||
def drillTipLength(tool):
|
||||
"""returns the length of the drillbit tip."""
|
||||
if tool.CuttingEdgeAngle == 180 or tool.CuttingEdgeAngle == 0.0 or tool.Diameter == 0.0:
|
||||
if tool.CuttingEdgeAngle == 180 or tool.CuttingEdgeAngle == 0.0 or float(tool.Diameter) == 0.0:
|
||||
return 0.0
|
||||
else:
|
||||
if tool.CuttingEdgeAngle <= 0 or tool.CuttingEdgeAngle >= 180:
|
||||
PathLog.error(translate("Path", "Invalid Cutting Edge Angle %.2f, must be >0° and <=180°") % tool.CuttingEdgeAngle)
|
||||
return 0.0
|
||||
theta = math.radians(tool.CuttingEdgeAngle)
|
||||
length = (tool.Diameter / 2) / math.tan(theta / 2)
|
||||
length = (float(tool.Diameter) / 2) / math.tan(theta / 2)
|
||||
if length < 0:
|
||||
PathLog.error(translate("Path", "Cutting Edge Angle (%.2f) results in negative tool tip length") % tool.CuttingEdgeAngle)
|
||||
return 0.0
|
||||
|
||||
@@ -34,8 +34,10 @@ PathLog.trackModule(PathLog.thisModule())
|
||||
|
||||
|
||||
class TestPathHelix(PathTestUtils.PathTestBase):
|
||||
RotateBy = 45
|
||||
|
||||
def setUp(self):
|
||||
self.clone = None
|
||||
self.doc = FreeCAD.open(FreeCAD.getHomePath() + 'Mod/Path/PathTests/test_holes00.fcstd')
|
||||
self.job = PathJob.Create('Job', [self.doc.Body])
|
||||
|
||||
@@ -68,7 +70,7 @@ class TestPathHelix(PathTestUtils.PathTestBase):
|
||||
proxy = op.Proxy
|
||||
model = self.job.Model.Group[0]
|
||||
|
||||
for deg in range(5, 360, 5):
|
||||
for deg in range(self.RotateBy, 360, self.RotateBy):
|
||||
model.Placement.Rotation = FreeCAD.Rotation(deg, 0, 0)
|
||||
for base in op.Base:
|
||||
model = base[0]
|
||||
@@ -81,7 +83,7 @@ class TestPathHelix(PathTestUtils.PathTestBase):
|
||||
def test03(self):
|
||||
'''Verify Helix generates proper holes for rotated base model'''
|
||||
|
||||
for deg in range(5, 360, 5):
|
||||
for deg in range(self.RotateBy, 360, self.RotateBy):
|
||||
self.tearDown()
|
||||
self.doc = FreeCAD.open(FreeCAD.getHomePath() + 'Mod/Path/PathTests/test_holes00.fcstd')
|
||||
self.doc.Body.Placement.Rotation = FreeCAD.Rotation(deg, 0, 0)
|
||||
@@ -102,7 +104,7 @@ class TestPathHelix(PathTestUtils.PathTestBase):
|
||||
|
||||
def test04(self):
|
||||
'''Verify Helix generates proper holes for rotated clone base model'''
|
||||
for deg in range(5, 360, 5):
|
||||
for deg in range(self.RotateBy, 360, self.RotateBy):
|
||||
self.tearDown()
|
||||
self.doc = FreeCAD.open(FreeCAD.getHomePath() + 'Mod/Path/PathTests/test_holes00.fcstd')
|
||||
self.clone = Draft.clone(self.doc.Body)
|
||||
|
||||
60
src/Mod/Path/PathTests/TestPathPreferences.py
Normal file
60
src/Mod/Path/PathTests/TestPathPreferences.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2019 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * 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'))
|
||||
60
src/Mod/Path/PathTests/TestPathToolBit.py
Normal file
60
src/Mod/Path/PathTests/TestPathToolBit.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# ***************************************************************************
|
||||
# * *
|
||||
# * Copyright (c) 2019 sliptonic <shopinthewoods@gmail.com> *
|
||||
# * *
|
||||
# * 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 shapee from file name'''
|
||||
|
||||
path = PathToolBit.findShape('endmill.fcstd')
|
||||
self.assertIsNot(path, None)
|
||||
self.assertNotEqual(path, 'endmill.fcstd')
|
||||
|
||||
def test01(self):
|
||||
'''Find a tool shapee from an invalid absolute path.'''
|
||||
|
||||
path = PathToolBit.findShape('/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')
|
||||
|
||||
|
||||
def test10(self):
|
||||
'''find the relative path of a tool bit'''
|
||||
shape = 'endmill.fcstd'
|
||||
path = PathToolBit.findShape(shape)
|
||||
self.assertIsNot(path, None)
|
||||
self.assertGreater(len(path), len(shape))
|
||||
rel = PathToolBit.findRelativePathShape(path)
|
||||
self.assertEqual(rel, shape)
|
||||
|
||||
def test11(self):
|
||||
'''store full path if relative path isn't found'''
|
||||
path = '/this/is/unlikely/a/valid/path/v-bit.fcstd'
|
||||
rel = PathToolBit.findRelativePathShape(path)
|
||||
self.assertEqual(rel, path)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
2
src/Mod/Path/Tools/.gitignore
vendored
Normal file
2
src/Mod/Path/Tools/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.fcstd1
|
||||
*.FCStd1
|
||||
12
src/Mod/Path/Tools/Bit/t1.fctb
Normal file
12
src/Mod/Path/Tools/Bit/t1.fctb
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "T1",
|
||||
"shape": "endmill.fcstd",
|
||||
"attribute": {},
|
||||
"parameter": {
|
||||
"CuttingEdgeHeight": "30.000 mm",
|
||||
"Diameter": "1.000 mm",
|
||||
"Length": "50.000 mm",
|
||||
"ShankDiameter": "3.000 mm"
|
||||
}
|
||||
}
|
||||
12
src/Mod/Path/Tools/Bit/t2.fctb
Normal file
12
src/Mod/Path/Tools/Bit/t2.fctb
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"name": "T2",
|
||||
"shape": "endmill.fcstd",
|
||||
"attribute": {},
|
||||
"parameter": {
|
||||
"CuttingEdgeHeight": "30.000 mm",
|
||||
"Diameter": "2.000 mm",
|
||||
"Length": "50.000 mm",
|
||||
"ShankDiameter": "3.000 mm"
|
||||
}
|
||||
}
|
||||
12
src/Mod/Path/Tools/Bit/t3.fctb
Normal file
12
src/Mod/Path/Tools/Bit/t3.fctb
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 2,
|
||||
"name": "T3",
|
||||
"shape": "endmill.fcstd",
|
||||
"attribute": {},
|
||||
"parameter": {
|
||||
"CuttingEdgeHeight": "30.000 mm",
|
||||
"Diameter": "3.000 mm",
|
||||
"Length": "50.000 mm",
|
||||
"ShankDiameter": "3.000 mm"
|
||||
}
|
||||
}
|
||||
12
src/Mod/Path/Tools/Bit/t4.fctb
Normal file
12
src/Mod/Path/Tools/Bit/t4.fctb
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"name": "T4",
|
||||
"shape": "endmill.fcstd",
|
||||
"attribute": {},
|
||||
"parameter": {
|
||||
"CuttingEdgeHeight": "30.000 mm",
|
||||
"Diameter": "4.000 mm",
|
||||
"Length": "50.000 mm",
|
||||
"ShankDiameter": "3.000 mm"
|
||||
}
|
||||
}
|
||||
12
src/Mod/Path/Tools/Bit/t5.fctb
Normal file
12
src/Mod/Path/Tools/Bit/t5.fctb
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"name": "T5",
|
||||
"shape": "endmill.fcstd",
|
||||
"attribute": {},
|
||||
"parameter": {
|
||||
"CuttingEdgeHeight": "30.000 mm",
|
||||
"Diameter": "5.000 mm",
|
||||
"Length": "50.000 mm",
|
||||
"ShankDiameter": "3.000 mm"
|
||||
}
|
||||
}
|
||||
12
src/Mod/Path/Tools/Bit/t6.fctb
Normal file
12
src/Mod/Path/Tools/Bit/t6.fctb
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"name": "T6",
|
||||
"shape": "endmill.fcstd",
|
||||
"attribute": {},
|
||||
"parameter": {
|
||||
"CuttingEdgeHeight": "30.000 mm",
|
||||
"Diameter": "6.000 mm",
|
||||
"Length": "50.000 mm",
|
||||
"ShankDiameter": "3.000 mm"
|
||||
}
|
||||
}
|
||||
12
src/Mod/Path/Tools/Bit/t7.fctb
Normal file
12
src/Mod/Path/Tools/Bit/t7.fctb
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"name": "T7",
|
||||
"shape": "endmill.fcstd",
|
||||
"attribute": {},
|
||||
"parameter": {
|
||||
"CuttingEdgeHeight": "30.000 mm",
|
||||
"Diameter": "7.000 mm",
|
||||
"Length": "50.000 mm",
|
||||
"ShankDiameter": "3.000 mm"
|
||||
}
|
||||
}
|
||||
12
src/Mod/Path/Tools/Bit/t8.fctb
Normal file
12
src/Mod/Path/Tools/Bit/t8.fctb
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"name": "T8",
|
||||
"shape": "endmill.fcstd",
|
||||
"attribute": {},
|
||||
"parameter": {
|
||||
"CuttingEdgeHeight": "30.000 mm",
|
||||
"Diameter": "8.000 mm",
|
||||
"Length": "50.000 mm",
|
||||
"ShankDiameter": "3.000 mm"
|
||||
}
|
||||
}
|
||||
12
src/Mod/Path/Tools/Bit/t9.fctb
Normal file
12
src/Mod/Path/Tools/Bit/t9.fctb
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"version": 1,
|
||||
"name": "T9",
|
||||
"shape": "endmill.fcstd",
|
||||
"attribute": {},
|
||||
"parameter": {
|
||||
"CuttingEdgeHeight": "30.000 mm",
|
||||
"Diameter": "9.000 mm",
|
||||
"Length": "50.000 mm",
|
||||
"ShankDiameter": "3.000 mm"
|
||||
}
|
||||
}
|
||||
41
src/Mod/Path/Tools/Library/endmills.fctl
Normal file
41
src/Mod/Path/Tools/Library/endmills.fctl
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"tools": [
|
||||
{
|
||||
"nr": 1,
|
||||
"path": "t1.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 2,
|
||||
"path": "t2.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 3,
|
||||
"path": "t3.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 4,
|
||||
"path": "t4.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 5,
|
||||
"path": "t5.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 6,
|
||||
"path": "t6.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 7,
|
||||
"path": "t7.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 8,
|
||||
"path": "t8.fctb"
|
||||
},
|
||||
{
|
||||
"nr": 9,
|
||||
"path": "t9.fctb"
|
||||
}
|
||||
],
|
||||
"version": 1
|
||||
}
|
||||
87
src/Mod/Path/Tools/README.md
Normal file
87
src/Mod/Path/Tools/README.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Tools
|
||||
|
||||
Each tool is stored as a JSON file which has the template's path and values for all named constraints of the template.
|
||||
It also includes all additional parameters and their values.
|
||||
|
||||
Storing a tool as a JSON file sounds great but eliminates the option of an accurate thumbnail. On the other hand,
|
||||
storing each tool as a `*.fcstd` file requires more space and does not allow for generating tools. If one has an
|
||||
extensive tool aresenal they might want to script the generation of tools which is easily done for a `*.json` file but
|
||||
practically impossible for `*.fcstd` files.
|
||||
|
||||
When a tool is instantiated in a job the PDN body is created from the template and the constraints are set according
|
||||
to the values from the JSON file. All additional parameters are created as properties on the object. This provides the
|
||||
the correct shape and dimensions which can be used to generate a point cloud or mesh for advanced algorithms (and
|
||||
potentially simulation).
|
||||
|
||||
# Tool Libraries
|
||||
|
||||
Due to each tool being stored in its own file and the storage/organization of those files being quite flexible the
|
||||
importance of a tool library for organisational purposes is quite diminished. The user is free to organise their tools
|
||||
in whichever directory hierarchy they see fit and can also name them as best fits their use and organisation. A
|
||||
_tool library_ is nevertheless a great representation for a physical grouping of tools, such as in an automatic tool
|
||||
changer.
|
||||
|
||||
A tool library is a (JSON) file with a mapping of tool id to the path of the tool file. As a consequence each tool
|
||||
can be in multiple libraries and doesn't have an `id` of it's own. The `id` is a property of the library.
|
||||
|
||||
If a tool from a tool library (or an entire tool library) is added to a job it retains its `id` from the library as a
|
||||
property. Adding a tool bit directly rsults in the tool getting the next free id assigned.
|
||||
|
||||
# Tool Controllers
|
||||
|
||||
They largely stay the same as they are today. As an additional feature it should be possible to _copy_ a TC, which
|
||||
allows for easy feed/speed changes for the same tool.
|
||||
|
||||
Above requirement highlights one change though, that the `id` should be a property of the Bit, and not of the TC.
|
||||
There are two requirements that are currently mapped to a single `id`. There needs to be an identification of which
|
||||
TC is being used by a certain op, and which tool number to use for a `M6` command.
|
||||
|
||||
# Paths and Extensibility
|
||||
|
||||
The following directory structure is used for supplied (shipped with FreeCAD) tools:
|
||||
```
|
||||
Tools
|
||||
+ Bit
|
||||
+ Library
|
||||
+ Shape
|
||||
```
|
||||
|
||||
Strictly speaking a user is free to store their tools wherever they want and however they want. By default the file
|
||||
dialog will open the corresponding directory (depending on context), or whichever directory the user opened last.
|
||||
|
||||
Above directory structure with the most common default tools shipped with FreeCAD should be installed analogous to
|
||||
TechDraw's templates.
|
||||
|
||||
## How to create a new tool
|
||||
|
||||
1. Set the tool's Label, this will show up in the object tree
|
||||
1. Select a tool shape from the existing templates. If your tool doesn't exist, you'll have to create a new template,
|
||||
see below for details.
|
||||
1. Each template has its own set of parameters, fill them with the tool's values.
|
||||
1. Select additional parameters
|
||||
1. Save the tool under path/file that makes sense to you
|
||||
|
||||
|
||||
## How to create a new tool bit Shape
|
||||
|
||||
A tool bit template represents the physical shape of a tool. It does not completely desribe the bit - for that some
|
||||
additional parameters are needed which will be added when an actual bit is parametrized from the template.
|
||||
|
||||
1. Create a new FreeCAD document
|
||||
1. Open the `PartDesign` workbench, create a body and give the body a label you want to show up in the bit selection.
|
||||
1. Create a sketch in the XZ plane and draw half the profile of the bit.
|
||||
* Put the top center of the bit on the origin (0,0)
|
||||
1. For any constraint serving as a parameter for the tool (like overall Length) create a named constraint
|
||||
* The name is the label of the input field
|
||||
* Names are split at CamelCase boundaries into words in the edit dialog
|
||||
* Use a `;` in the name to add help text which will show up as the entry fields tool tip
|
||||
* If the tool is used by legacy ops it should at least have one constraint called `Diameter`
|
||||
* Use construction lines for constraints that are not directly accessible, like `Diameter` and `Angle`
|
||||
1. Any unnamed constraint will not be editable for a specific tool
|
||||
1. Once the sketch is fully constrained, close the sketch
|
||||
1. Rotate the sketch around the z-axis
|
||||
1. Save the document as a new file in the Shape directory
|
||||
* Before saving the document make sure you have _Save Thumbnail_ selected, and _Add program logo_ deselected in
|
||||
FreeCAD's preferences.
|
||||
* Also make sure to switch to _Front View_ and _Fit content to screen_
|
||||
* Whatever you see when saving the document will end up being the visual representation of the template
|
||||
BIN
src/Mod/Path/Tools/Shape/ballend.fcstd
Normal file
BIN
src/Mod/Path/Tools/Shape/ballend.fcstd
Normal file
Binary file not shown.
BIN
src/Mod/Path/Tools/Shape/bullnose.fcstd
Normal file
BIN
src/Mod/Path/Tools/Shape/bullnose.fcstd
Normal file
Binary file not shown.
BIN
src/Mod/Path/Tools/Shape/drill.fcstd
Normal file
BIN
src/Mod/Path/Tools/Shape/drill.fcstd
Normal file
Binary file not shown.
BIN
src/Mod/Path/Tools/Shape/endmill.fcstd
Normal file
BIN
src/Mod/Path/Tools/Shape/endmill.fcstd
Normal file
Binary file not shown.
BIN
src/Mod/Path/Tools/Shape/v-bit.fcstd
Normal file
BIN
src/Mod/Path/Tools/Shape/v-bit.fcstd
Normal file
Binary file not shown.
@@ -56,6 +56,7 @@ EXTERNAL_MODULES+=' Path'
|
||||
EXTERNAL_MODULES+=' PySide'
|
||||
EXTERNAL_MODULES+=' PySide.QtCore'
|
||||
EXTERNAL_MODULES+=' PySide.QtGui'
|
||||
EXTERNAL_MODULES+=' Sketcher'
|
||||
EXTERNAL_MODULES+=' TechDraw'
|
||||
EXTERNAL_MODULES+=' TestSketcherApp'
|
||||
EXTERNAL_MODULES+=' area'
|
||||
|
||||
Reference in New Issue
Block a user