Threadmilling translation cleanup
This commit is contained in:
@@ -30,18 +30,8 @@
|
||||
<item row="0" column="1">
|
||||
<widget class="QComboBox" name="threadOrientation">
|
||||
<property name="currentIndex">
|
||||
<number>1</number>
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Left Hand</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Right Hand</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
@@ -52,23 +42,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="threadType">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Custom</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Metric - internal</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>SAE - internal</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="threadType"/>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QComboBox" name="threadName"/>
|
||||
@@ -208,18 +182,7 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="opDirection">
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Climb</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Conventional</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
<widget class="QComboBox" name="opDirection"/>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QCheckBox" name="leadInOut">
|
||||
@@ -236,7 +199,7 @@
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>Gui::QuantitySpinBox</class>
|
||||
<extends>QDoubleSpinBox</extends>
|
||||
<extends>QWidget</extends>
|
||||
<header>Gui/QuantitySpinBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
|
||||
@@ -150,7 +150,7 @@ class PathWorkbench(Workbench):
|
||||
projcmdlist.append("Path_Sanity")
|
||||
prepcmdlist.append("Path_Shape")
|
||||
extracmdlist.extend(["Path_Area", "Path_Area_Workplane"])
|
||||
specialcmdlist.append("Path_Thread_Milling")
|
||||
specialcmdlist.append("Path_ThreadMilling")
|
||||
twodopcmdlist.append("Path_Slot")
|
||||
|
||||
if PathPreferences.advancedOCLFeaturesEnabled():
|
||||
|
||||
@@ -402,7 +402,7 @@ def select(op):
|
||||
opsel["Vcarve"] = vcarveselect
|
||||
opsel["Probe"] = probeselect
|
||||
opsel["Custom"] = customselect
|
||||
opsel["Thread Milling"] = drillselect
|
||||
opsel["ThreadMilling"] = drillselect
|
||||
opsel["TurnFace"] = turnselect
|
||||
opsel["TurnProfile"] = turnselect
|
||||
opsel["TurnPartoff"] = turnselect
|
||||
|
||||
@@ -28,26 +28,25 @@ import PathScripts.PathCircularHoleBase as PathCircularHoleBase
|
||||
import PathScripts.PathGeom as PathGeom
|
||||
import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOp as PathOp
|
||||
import PathScripts.PathUtils as PathUtils
|
||||
import math
|
||||
|
||||
from PySide import QtCore
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
__title__ = "Path Thread Milling Operation"
|
||||
__author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
__doc__ = "Path thread milling operation."
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
# Qt translation handling
|
||||
def translate(context, text, disambig=None):
|
||||
return QtCore.QCoreApplication.translate(context, text, disambig)
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
|
||||
def radiiInternal(majorDia, minorDia, toolDia, toolCrest = None):
|
||||
'''internlThreadRadius(majorDia, minorDia, toolDia, toolCrest) ... returns the maximum radius for thread.'''
|
||||
def radiiInternal(majorDia, minorDia, toolDia, toolCrest=None):
|
||||
"""internlThreadRadius(majorDia, minorDia, toolDia, toolCrest) ... returns the maximum radius for thread."""
|
||||
PathLog.track(majorDia, minorDia, toolDia, toolCrest)
|
||||
if toolCrest is None:
|
||||
toolCrest = 0.0
|
||||
@@ -57,20 +56,25 @@ def radiiInternal(majorDia, minorDia, toolDia, toolCrest = None):
|
||||
# - The major diameter is 3/8 * H bigger than the pitch diameter
|
||||
# Since we already have the outer diameter it's simpler to just add 1/8 * H
|
||||
# to get the outer tip of the thread.
|
||||
H = ((majorDia - minorDia) / 2.0 ) * 1.6 # (D - d)/2 = 5/8 * H
|
||||
H = ((majorDia - minorDia) / 2.0) * 1.6 # (D - d)/2 = 5/8 * H
|
||||
outerTip = majorDia / 2.0 + H / 8.0
|
||||
# Compensate for the crest of the tool
|
||||
toolTip = outerTip - toolCrest * 0.8660254037844386 # math.sqrt(3)/2 ... 60deg triangle height
|
||||
toolTip = (
|
||||
outerTip - toolCrest * 0.8660254037844386
|
||||
) # math.sqrt(3)/2 ... 60deg triangle height
|
||||
return ((minorDia - toolDia) / 2.0, toolTip - toolDia / 2.0)
|
||||
|
||||
def threadPasses(count, radii, majorDia, minorDia, toolDia, toolCrest = None):
|
||||
|
||||
def threadPasses(count, radii, majorDia, minorDia, toolDia, toolCrest=None):
|
||||
PathLog.track(count, radii, majorDia, minorDia, toolDia, toolCrest)
|
||||
minor, major = radii(majorDia, minorDia, toolDia, toolCrest)
|
||||
dr = float(major - minor) / count
|
||||
dr = float(major - minor) / count
|
||||
return [major - dr * (count - (i + 1)) for i in range(count)]
|
||||
|
||||
|
||||
class _InternalThread(object):
|
||||
'''Helper class for dealing with different thread types'''
|
||||
"""Helper class for dealing with different thread types"""
|
||||
|
||||
def __init__(self, cmd, zStart, zFinal, pitch):
|
||||
self.cmd = cmd
|
||||
if zStart < zFinal:
|
||||
@@ -82,33 +86,34 @@ class _InternalThread(object):
|
||||
self.zFinal = zFinal
|
||||
|
||||
def overshoots(self, z):
|
||||
'''overshoots(z) ... returns true if adding another half helix goes beyond the thread bounds'''
|
||||
"""overshoots(z) ... returns true if adding another half helix goes beyond the thread bounds"""
|
||||
if self.pitch < 0:
|
||||
return z + self.hPitch < self.zFinal
|
||||
return z + self.hPitch > self.zFinal
|
||||
|
||||
def adjustX(self, x, dx):
|
||||
'''adjustX(x, dx) ... move x by dx, the direction depends on the thread settings'''
|
||||
"""adjustX(x, dx) ... move x by dx, the direction depends on the thread settings"""
|
||||
if self.isG3() == (self.pitch > 0):
|
||||
return x + dx
|
||||
return x - dx
|
||||
|
||||
def adjustY(self, y, dy):
|
||||
'''adjustY(y, dy) ... move y by dy, the direction depends on the thread settings'''
|
||||
"""adjustY(y, dy) ... move y by dy, the direction depends on the thread settings"""
|
||||
if self.isG3():
|
||||
return y - dy
|
||||
return y - dy
|
||||
|
||||
def isG3(self):
|
||||
'''isG3() ... returns True if this is a G3 command'''
|
||||
return self.cmd in ['G3', 'G03', 'g3', 'g03']
|
||||
"""isG3() ... returns True if this is a G3 command"""
|
||||
return self.cmd in ["G3", "G03", "g3", "g03"]
|
||||
|
||||
def isUp(self):
|
||||
'''isUp() ... returns True if the thread goes from the bottom up'''
|
||||
"""isUp() ... returns True if the thread goes from the bottom up"""
|
||||
return self.pitch > 0
|
||||
|
||||
|
||||
def internalThreadCommands(loc, cmd, zStart, zFinal, pitch, radius, leadInOut):
|
||||
'''internalThreadCommands(loc, cmd, zStart, zFinal, pitch, radius) ... returns the g-code to mill the given internal thread'''
|
||||
"""internalThreadCommands(loc, cmd, zStart, zFinal, pitch, radius) ... returns the g-code to mill the given internal thread"""
|
||||
thread = _InternalThread(cmd, zStart, zFinal, pitch)
|
||||
|
||||
yMin = loc.y - radius
|
||||
@@ -118,30 +123,29 @@ def internalThreadCommands(loc, cmd, zStart, zFinal, pitch, radius, leadInOut):
|
||||
# at this point the tool is at a safe height (depending on the previous thread), so we can move
|
||||
# into position first, and then drop to the start height. If there is any material in the way this
|
||||
# op hasn't been setup properly.
|
||||
path.append(Path.Command('G0', {'X': loc.x, 'Y': loc.y}))
|
||||
path.append(Path.Command('G0', {'Z': thread.zStart}))
|
||||
path.append(Path.Command("G0", {"X": loc.x, "Y": loc.y}))
|
||||
path.append(Path.Command("G0", {"Z": thread.zStart}))
|
||||
if leadInOut:
|
||||
path.append(Path.Command(thread.cmd, {'Y': yMax, 'J': (yMax - loc.y) / 2}))
|
||||
path.append(Path.Command(thread.cmd, {"Y": yMax, "J": (yMax - loc.y) / 2}))
|
||||
else:
|
||||
path.append(Path.Command('G1', {'Y': yMax}))
|
||||
path.append(Path.Command("G1", {"Y": yMax}))
|
||||
|
||||
z = thread.zStart
|
||||
r = -radius
|
||||
i = 0
|
||||
while True:
|
||||
z = thread.zStart + i*thread.hPitch
|
||||
z = thread.zStart + i * thread.hPitch
|
||||
if thread.overshoots(z):
|
||||
break
|
||||
if 0 == (i & 0x01):
|
||||
y = yMin
|
||||
else:
|
||||
y = yMax
|
||||
path.append(Path.Command(thread.cmd, {'Y': y, 'Z': z + thread.hPitch, 'J': r}))
|
||||
path.append(Path.Command(thread.cmd, {"Y": y, "Z": z + thread.hPitch, "J": r}))
|
||||
r = -r
|
||||
i = i + 1
|
||||
|
||||
|
||||
z = thread.zStart + i*thread.hPitch
|
||||
z = thread.zStart + i * thread.hPitch
|
||||
if PathGeom.isRoughly(z, thread.zFinal):
|
||||
x = loc.x
|
||||
else:
|
||||
@@ -151,48 +155,183 @@ def internalThreadCommands(loc, cmd, zStart, zFinal, pitch, radius, leadInOut):
|
||||
dx = math.sin(k * math.pi)
|
||||
y = thread.adjustY(loc.y, r * dy)
|
||||
x = thread.adjustX(loc.x, r * dx)
|
||||
path.append(Path.Command(thread.cmd, {'X': x, 'Y': y, 'Z': thread.zFinal, 'J': r}))
|
||||
path.append(
|
||||
Path.Command(thread.cmd, {"X": x, "Y": y, "Z": thread.zFinal, "J": r})
|
||||
)
|
||||
|
||||
if leadInOut:
|
||||
path.append(Path.Command(thread.cmd, {'X': loc.x, 'Y': loc.y, 'I': (loc.x - x) / 2, 'J': (loc.y - y) / 2}))
|
||||
path.append(
|
||||
Path.Command(
|
||||
thread.cmd,
|
||||
{"X": loc.x, "Y": loc.y, "I": (loc.x - x) / 2, "J": (loc.y - y) / 2},
|
||||
)
|
||||
)
|
||||
else:
|
||||
path.append(Path.Command('G1', {'X': loc.x, 'Y': loc.y}))
|
||||
path.append(Path.Command("G1", {"X": loc.x, "Y": loc.y}))
|
||||
return path
|
||||
|
||||
class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
'''Proxy object for thread milling operation.'''
|
||||
|
||||
LeftHand = 'LeftHand'
|
||||
RightHand = 'RightHand'
|
||||
ThreadTypeCustom = 'Custom'
|
||||
ThreadTypeMetricInternal = 'Metric - internal'
|
||||
ThreadTypeImperialInternal = 'Imperial - internal'
|
||||
DirectionClimb = 'Climb'
|
||||
DirectionConventional = 'Conventional'
|
||||
class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
"""Proxy object for thread milling operation."""
|
||||
|
||||
LeftHand = "LeftHand"
|
||||
RightHand = "RightHand"
|
||||
ThreadTypeCustom = "Custom"
|
||||
ThreadTypeMetricInternal = "MetricInternal"
|
||||
ThreadTypeImperialInternal = "ImperialInternal"
|
||||
DirectionClimb = "Climb"
|
||||
DirectionConventional = "Conventional"
|
||||
|
||||
ThreadOrientations = [LeftHand, RightHand]
|
||||
ThreadTypes = [ThreadTypeCustom, ThreadTypeMetricInternal, ThreadTypeImperialInternal]
|
||||
Directions = [DirectionClimb, DirectionConventional]
|
||||
ThreadTypes = [
|
||||
ThreadTypeCustom,
|
||||
ThreadTypeMetricInternal,
|
||||
ThreadTypeImperialInternal,
|
||||
]
|
||||
Directions = [DirectionClimb, DirectionConventional]
|
||||
|
||||
@classmethod
|
||||
def propertyEnumerations(self, dataType="data"):
|
||||
"""helixOpPropertyEnumerations(dataType="data")... return property enumeration lists of specified dataType.
|
||||
Args:
|
||||
dataType = 'data', 'raw', 'translated'
|
||||
Notes:
|
||||
'data' is list of internal string literals used in code
|
||||
'raw' is list of (translated_text, data_string) tuples
|
||||
'translated' is list of translated string literals
|
||||
"""
|
||||
|
||||
# Enumeration lists for App::PropertyEnumeration properties
|
||||
enums = {
|
||||
"ThreadType": [
|
||||
(translate("Path_ThreadMilling", "Custom"), "Custom"),
|
||||
(translate("Path_ThreadMilling", "Metric Internal"), "MetricInternal"),
|
||||
(
|
||||
translate("Path_ThreadMilling", "Imperial Internal"),
|
||||
"ImperialInternal",
|
||||
),
|
||||
], # this is the direction that the profile runs
|
||||
"ThreadOrientation": [
|
||||
(translate("Path_ThreadMilling", "LeftHand"), "LeftHand"),
|
||||
(translate("Path_ThreadMilling", "RightHand"), "RightHand"),
|
||||
], # side of profile that cutter is on in relation to direction of profile
|
||||
"Direction": [
|
||||
(translate("Path_ThreadMilling", "Climb"), "Climb"),
|
||||
(translate("Path_ThreadMilling", "Conventional"), "Conventional"),
|
||||
], # side of profile that cutter is on in relation to direction of profile
|
||||
}
|
||||
|
||||
if dataType == "raw":
|
||||
return enums
|
||||
|
||||
data = list()
|
||||
idx = 0 if dataType == "translated" else 1
|
||||
|
||||
PathLog.debug(enums)
|
||||
|
||||
for k, v in enumerate(enums):
|
||||
data.append((v, [tup[idx] for tup in enums[v]]))
|
||||
PathLog.debug(data)
|
||||
|
||||
return data
|
||||
|
||||
def circularHoleFeatures(self, obj):
|
||||
return PathOp.FeatureBaseGeometry
|
||||
|
||||
def initCircularHoleOperation(self, obj):
|
||||
obj.addProperty("App::PropertyEnumeration", "ThreadOrientation", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread orientation"))
|
||||
obj.ThreadOrientation = self.ThreadOrientations
|
||||
obj.addProperty("App::PropertyEnumeration", "ThreadType", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Currently only internal"))
|
||||
obj.ThreadType = self.ThreadTypes
|
||||
obj.addProperty("App::PropertyString", "ThreadName", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Defines which standard thread was chosen"))
|
||||
obj.addProperty("App::PropertyLength", "MajorDiameter", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread's major diameter"))
|
||||
obj.addProperty("App::PropertyLength", "MinorDiameter", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread's minor diameter"))
|
||||
obj.addProperty("App::PropertyLength", "Pitch", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread's pitch - used for metric threads"))
|
||||
obj.addProperty("App::PropertyInteger", "TPI", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set thread's TPI (turns per inch) - used for imperial threads"))
|
||||
obj.addProperty("App::PropertyInteger", "ThreadFit", "Thread", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set how many passes are used to cut the thread"))
|
||||
obj.addProperty("App::PropertyInteger", "Passes", "Operation", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set how many passes are used to cut the thread"))
|
||||
obj.addProperty("App::PropertyEnumeration", "Direction", "Operation", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Direction of thread cutting operation"))
|
||||
obj.addProperty("App::PropertyBool", "LeadInOut", "Operation", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Set to True to get lead in and lead out arcs at the start and end of the thread cut"))
|
||||
obj.addProperty("App::PropertyLink", "ClearanceOp", "Operation", QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Operation to clear the inside of the thread"))
|
||||
obj.Direction = self.Directions
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"ThreadOrientation",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Set thread orientation"),
|
||||
)
|
||||
# obj.ThreadOrientation = self.ThreadOrientations
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"ThreadType",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Currently only internal"),
|
||||
)
|
||||
# obj.ThreadType = self.ThreadTypes
|
||||
obj.addProperty(
|
||||
"App::PropertyString",
|
||||
"ThreadName",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Defines which standard thread was chosen"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLength",
|
||||
"MajorDiameter",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Set thread's major diameter"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLength",
|
||||
"MinorDiameter",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Set thread's minor diameter"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLength",
|
||||
"Pitch",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Set thread's pitch - used for metric threads"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyInteger",
|
||||
"TPI",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set thread's TPI (turns per inch) - used for imperial threads",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyInteger",
|
||||
"ThreadFit",
|
||||
"Thread",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Set how many passes are used to cut the thread"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyInteger",
|
||||
"Passes",
|
||||
"Operation",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Set how many passes are used to cut the thread"
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyEnumeration",
|
||||
"Direction",
|
||||
"Operation",
|
||||
QT_TRANSLATE_NOOP("App::Property", "Direction of thread cutting operation"),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyBool",
|
||||
"LeadInOut",
|
||||
"Operation",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property",
|
||||
"Set to True to get lead in and lead out arcs at the start and end of the thread cut",
|
||||
),
|
||||
)
|
||||
obj.addProperty(
|
||||
"App::PropertyLink",
|
||||
"ClearanceOp",
|
||||
"Operation",
|
||||
QT_TRANSLATE_NOOP(
|
||||
"App::Property", "Operation to clear the inside of the thread"
|
||||
),
|
||||
)
|
||||
|
||||
for n in self.propertyEnumerations():
|
||||
setattr(obj, n[0], n[1])
|
||||
|
||||
def threadStartDepth(self, obj):
|
||||
if obj.ThreadOrientation == self.RightHand:
|
||||
@@ -225,25 +364,25 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
PathLog.track(obj.Label)
|
||||
if obj.ThreadOrientation == self.RightHand:
|
||||
if obj.Direction == self.DirectionClimb:
|
||||
PathLog.track(obj.Label, 'G2')
|
||||
return 'G2'
|
||||
PathLog.track(obj.Label, 'G3')
|
||||
return 'G3'
|
||||
PathLog.track(obj.Label, "G2")
|
||||
return "G2"
|
||||
PathLog.track(obj.Label, "G3")
|
||||
return "G3"
|
||||
if obj.Direction == self.DirectionClimb:
|
||||
PathLog.track(obj.Label, 'G3')
|
||||
return 'G3'
|
||||
PathLog.track(obj.Label, 'G2')
|
||||
return 'G2'
|
||||
PathLog.track(obj.Label, "G3")
|
||||
return "G3"
|
||||
PathLog.track(obj.Label, "G2")
|
||||
return "G2"
|
||||
|
||||
def threadSetup(self, obj):
|
||||
# the thing to remember is that Climb, for an internal thread must always be G3
|
||||
if obj.Direction == self.DirectionClimb:
|
||||
if obj.ThreadOrientation == self.RightHand:
|
||||
return ('G3', obj.FinalDepth.Value, obj.StartDepth.Value)
|
||||
return ('G3', obj.StartDepth.Value, obj.FinalDepth.Value)
|
||||
return ("G3", obj.FinalDepth.Value, obj.StartDepth.Value)
|
||||
return ("G3", obj.StartDepth.Value, obj.FinalDepth.Value)
|
||||
if obj.ThreadOrientation == self.RightHand:
|
||||
return ('G2', obj.StartDepth.Value, obj.FinalDepth.Value)
|
||||
return ('G2', obj.FinalDepth.Value, obj.StartDepth.Value)
|
||||
return ("G2", obj.StartDepth.Value, obj.FinalDepth.Value)
|
||||
return ("G2", obj.FinalDepth.Value, obj.StartDepth.Value)
|
||||
|
||||
def threadPassRadii(self, obj):
|
||||
PathLog.track(obj.Label)
|
||||
@@ -251,7 +390,7 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
rMinor = (obj.MinorDiameter.Value - self.tool.Diameter) / 2.0
|
||||
if obj.Passes < 1:
|
||||
obj.Passes = 1
|
||||
rPass = (rMajor - rMinor) / obj.Passes
|
||||
rPass = (rMajor - rMinor) / obj.Passes
|
||||
passes = [rMajor]
|
||||
for i in range(1, obj.Passes):
|
||||
passes.append(rMajor - rPass * i)
|
||||
@@ -260,20 +399,33 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
def executeThreadMill(self, obj, loc, gcode, zStart, zFinal, pitch):
|
||||
PathLog.track(obj.Label, loc, gcode, zStart, zFinal, pitch)
|
||||
|
||||
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
|
||||
self.commandlist.append(
|
||||
Path.Command("G0", {"Z": obj.ClearanceHeight.Value, "F": self.vertRapid})
|
||||
)
|
||||
|
||||
for radius in threadPasses(obj.Passes, radiiInternal, obj.MajorDiameter.Value, obj.MinorDiameter.Value, float(self.tool.Diameter), float(self.tool.Crest)):
|
||||
commands = internalThreadCommands(loc, gcode, zStart, zFinal, pitch, radius, obj.LeadInOut)
|
||||
for radius in threadPasses(
|
||||
obj.Passes,
|
||||
radiiInternal,
|
||||
obj.MajorDiameter.Value,
|
||||
obj.MinorDiameter.Value,
|
||||
float(self.tool.Diameter),
|
||||
float(self.tool.Crest),
|
||||
):
|
||||
commands = internalThreadCommands(
|
||||
loc, gcode, zStart, zFinal, pitch, radius, obj.LeadInOut
|
||||
)
|
||||
for cmd in commands:
|
||||
p = cmd.Parameters
|
||||
if cmd.Name in ['G0']:
|
||||
p.update({'F': self.vertRapid})
|
||||
if cmd.Name in ['G1', 'G2', 'G3']:
|
||||
p.update({'F': self.horizFeed})
|
||||
if cmd.Name in ["G0"]:
|
||||
p.update({"F": self.vertRapid})
|
||||
if cmd.Name in ["G1", "G2", "G3"]:
|
||||
p.update({"F": self.horizFeed})
|
||||
cmd.Parameters = p
|
||||
self.commandlist.extend(commands)
|
||||
|
||||
self.commandlist.append(Path.Command('G0', {'Z': obj.ClearanceHeight.Value, 'F': self.vertRapid}))
|
||||
self.commandlist.append(
|
||||
Path.Command("G0", {"Z": obj.ClearanceHeight.Value, "F": self.vertRapid})
|
||||
)
|
||||
|
||||
def circularHoleExecute(self, obj, holes):
|
||||
PathLog.track()
|
||||
@@ -290,11 +442,17 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
|
||||
# rapid to clearance height
|
||||
for loc in holes:
|
||||
self.executeThreadMill(obj, FreeCAD.Vector(loc['x'], loc['y'], 0), cmd, zStart, zFinal, pitch)
|
||||
self.executeThreadMill(
|
||||
obj,
|
||||
FreeCAD.Vector(loc["x"], loc["y"], 0),
|
||||
cmd,
|
||||
zStart,
|
||||
zFinal,
|
||||
pitch,
|
||||
)
|
||||
else:
|
||||
PathLog.error("No suitable Tool found for thread milling operation")
|
||||
|
||||
|
||||
def opSetDefaultValues(self, obj, job):
|
||||
obj.ThreadOrientation = self.RightHand
|
||||
obj.ThreadType = self.ThreadTypeMetricInternal
|
||||
@@ -306,8 +464,8 @@ class ObjectThreadMilling(PathCircularHoleBase.ObjectOp):
|
||||
obj.LeadInOut = True
|
||||
|
||||
def isToolSupported(self, obj, tool):
|
||||
'''Thread milling only supports thread milling cutters.'''
|
||||
return hasattr(tool, 'Diameter') and hasattr(tool, 'Crest')
|
||||
"""Thread milling only supports thread milling cutters."""
|
||||
return hasattr(tool, "Diameter") and hasattr(tool, "Crest")
|
||||
|
||||
|
||||
def SetupProperties():
|
||||
@@ -327,11 +485,10 @@ def SetupProperties():
|
||||
|
||||
|
||||
def Create(name, obj=None, parentJob=None):
|
||||
'''Create(name) ... Creates and returns a thread milling operation.'''
|
||||
"""Create(name) ... Creates and returns a thread milling operation."""
|
||||
if obj is None:
|
||||
obj = FreeCAD.ActiveDocument.addObject("Path::FeaturePython", name)
|
||||
obj.Proxy = ObjectThreadMilling(obj, name, parentJob)
|
||||
if obj.Proxy:
|
||||
obj.Proxy.findAllHoles(obj)
|
||||
return obj
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
import FreeCAD
|
||||
import FreeCADGui
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathGui as PGui # ensure Path/Gui/Resources are loaded
|
||||
import PathScripts.PathCircularHoleBaseGui as PathCircularHoleBaseGui
|
||||
import PathScripts.PathThreadMilling as PathThreadMilling
|
||||
import PathScripts.PathGui as PathGui
|
||||
@@ -30,6 +30,9 @@ import PathScripts.PathLog as PathLog
|
||||
import PathScripts.PathOpGui as PathOpGui
|
||||
import csv
|
||||
|
||||
from PySide.QtCore import QT_TRANSLATE_NOOP
|
||||
|
||||
|
||||
from PySide import QtCore
|
||||
|
||||
__title__ = "Path Thread Milling Operation UI."
|
||||
@@ -37,52 +40,88 @@ __author__ = "sliptonic (Brad Collette)"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
__doc__ = "UI and Command for Path Thread Milling Operation."
|
||||
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
#PathLog.trackModule(PathLog.thisModule())
|
||||
if False:
|
||||
PathLog.setLevel(PathLog.Level.DEBUG, PathLog.thisModule())
|
||||
PathLog.trackModule(PathLog.thisModule())
|
||||
else:
|
||||
PathLog.setLevel(PathLog.Level.INFO, PathLog.thisModule())
|
||||
|
||||
translate = FreeCAD.Qt.translate
|
||||
|
||||
def setupCombo(combo, selections):
|
||||
combo.clear()
|
||||
for item in selections:
|
||||
combo.addItem(item)
|
||||
|
||||
def fillThreads(combo, dataFile):
|
||||
combo.blockSignals(True)
|
||||
combo.clear()
|
||||
with open("{}Mod/Path/Data/Threads/{}.csv".format(FreeCAD.getHomePath(), dataFile)) as fp:
|
||||
with open(
|
||||
"{}Mod/Path/Data/Threads/{}.csv".format(FreeCAD.getHomePath(), dataFile)
|
||||
) as fp:
|
||||
reader = csv.DictReader(fp)
|
||||
for row in reader:
|
||||
combo.addItem(row['name'], row)
|
||||
combo.addItem(row["name"], row)
|
||||
combo.setEnabled(True)
|
||||
combo.blockSignals(False)
|
||||
|
||||
|
||||
class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
'''Controller for the thread milling operation's page'''
|
||||
"""Controller for the thread milling operation's page"""
|
||||
|
||||
def initPage(self, obj):
|
||||
self.majorDia = PathGui.QuantitySpinBox(self.form.threadMajor, obj, 'MajorDiameter') # pylint: disable=attribute-defined-outside-init
|
||||
self.minorDia = PathGui.QuantitySpinBox(self.form.threadMinor, obj, 'MinorDiameter') # pylint: disable=attribute-defined-outside-init
|
||||
self.pitch = PathGui.QuantitySpinBox(self.form.threadPitch, obj, 'Pitch') # pylint: disable=attribute-defined-outside-init
|
||||
self.majorDia = PathGui.QuantitySpinBox(
|
||||
self.form.threadMajor, obj, "MajorDiameter"
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
self.minorDia = PathGui.QuantitySpinBox(
|
||||
self.form.threadMinor, obj, "MinorDiameter"
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
self.pitch = PathGui.QuantitySpinBox(
|
||||
self.form.threadPitch, obj, "Pitch"
|
||||
) # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
setupCombo(self.form.threadOrientation, obj.Proxy.ThreadOrientations)
|
||||
setupCombo(self.form.threadType, obj.Proxy.ThreadTypes)
|
||||
setupCombo(self.form.opDirection, obj.Proxy.Directions)
|
||||
# setupCombo(self.form.threadOrientation, obj.Proxy.ThreadOrientations)
|
||||
# setupCombo(self.form.threadType, obj.Proxy.ThreadTypes)
|
||||
# setupCombo(self.form.opDirection, obj.Proxy.Directions)
|
||||
|
||||
def getForm(self):
|
||||
'''getForm() ... return UI'''
|
||||
return FreeCADGui.PySideUic.loadUi(":/panels/PageOpThreadMillingEdit.ui")
|
||||
"""getForm() ... return UI"""
|
||||
form = FreeCADGui.PySideUic.loadUi(":/panels/PageOpThreadMillingEdit.ui")
|
||||
comboToPropertyMap = [
|
||||
("threadOrientation", "ThreadOrientation"),
|
||||
("threadType", "ThreadType"),
|
||||
("opDirection", "Direction"),
|
||||
]
|
||||
enumTups = PathThreadMilling.ObjectThreadMilling.propertyEnumerations(
|
||||
dataType="raw"
|
||||
)
|
||||
self.populateCombobox(form, enumTups, comboToPropertyMap)
|
||||
|
||||
return form
|
||||
|
||||
def populateCombobox(self, form, enumTups, comboBoxesPropertyMap):
|
||||
"""fillComboboxes(form, comboBoxesPropertyMap) ... populate comboboxes with translated enumerations
|
||||
** comboBoxesPropertyMap will be unnecessary if UI files use strict combobox naming protocol.
|
||||
Args:
|
||||
form = UI form
|
||||
enumTups = list of (translated_text, data_string) tuples
|
||||
comboBoxesPropertyMap = list of (translated_text, data_string) tuples
|
||||
"""
|
||||
# Load appropriate enumerations in each combobox
|
||||
for cb, prop in comboBoxesPropertyMap:
|
||||
box = getattr(form, cb) # Get the combobox
|
||||
box.clear() # clear the combobox
|
||||
for text, data in enumTups[prop]: # load enumerations
|
||||
box.addItem(text, data)
|
||||
|
||||
def getFields(self, obj):
|
||||
'''getFields(obj) ... update obj's properties with values from the UI'''
|
||||
"""getFields(obj) ... update obj's properties with values from the UI"""
|
||||
PathLog.track()
|
||||
|
||||
self.majorDia.updateProperty()
|
||||
self.minorDia.updateProperty()
|
||||
self.pitch.updateProperty()
|
||||
|
||||
obj.ThreadOrientation = self.form.threadOrientation.currentText()
|
||||
obj.ThreadType = self.form.threadType.currentText()
|
||||
obj.ThreadOrientation = self.form.threadOrientation.currentData()
|
||||
obj.ThreadType = self.form.threadType.currentData()
|
||||
obj.ThreadName = self.form.threadName.currentText()
|
||||
obj.Direction = self.form.opDirection.currentText()
|
||||
obj.Direction = self.form.opDirection.currentData()
|
||||
obj.Passes = self.form.opPasses.value()
|
||||
obj.LeadInOut = self.form.leadInOut.checkState() == QtCore.Qt.Checked
|
||||
obj.TPI = self.form.threadTPI.value()
|
||||
@@ -90,23 +129,22 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
self.updateToolController(obj, self.form.toolController)
|
||||
|
||||
def setFields(self, obj):
|
||||
'''setFields(obj) ... update UI with obj properties' values'''
|
||||
"""setFields(obj) ... update UI with obj properties' values"""
|
||||
PathLog.track()
|
||||
|
||||
self.form.threadOrientation.setCurrentText(obj.ThreadOrientation)
|
||||
self.selectInComboBox(obj.ThreadOrientation, self.form.threadOrientation)
|
||||
self.selectInComboBox(obj.ThreadType, self.form.threadType)
|
||||
self.selectInComboBox(obj.Direction, self.form.opDirection)
|
||||
|
||||
self.form.threadType.blockSignals(True)
|
||||
self.form.threadName.blockSignals(True)
|
||||
self.form.threadType.setCurrentText(obj.ThreadType)
|
||||
self._updateFromThreadType()
|
||||
self.form.threadName.setCurrentText(obj.ThreadName)
|
||||
self.form.threadType.blockSignals(False)
|
||||
self.form.threadName.blockSignals(False)
|
||||
self.form.threadTPI.setValue(obj.TPI)
|
||||
|
||||
self.form.opPasses.setValue(obj.Passes)
|
||||
self.form.opDirection.setCurrentText(obj.Direction)
|
||||
self.form.leadInOut.setCheckState(QtCore.Qt.Checked if obj.LeadInOut else QtCore.Qt.Unchecked)
|
||||
self.form.leadInOut.setCheckState(
|
||||
QtCore.Qt.Checked if obj.LeadInOut else QtCore.Qt.Unchecked
|
||||
)
|
||||
|
||||
self.majorDia.updateSpinBox()
|
||||
self.minorDia.updateSpinBox()
|
||||
@@ -114,16 +152,24 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
|
||||
self.setupToolController(obj, self.form.toolController)
|
||||
|
||||
|
||||
def _isThreadMetric(self):
|
||||
return self.form.threadType.currentText() == PathThreadMilling.ObjectThreadMilling.ThreadTypeMetricInternal
|
||||
return (
|
||||
self.form.threadType.currentData()
|
||||
== PathThreadMilling.ObjectThreadMilling.ThreadTypeMetricInternal
|
||||
)
|
||||
|
||||
def _isThreadImperial(self):
|
||||
return self.form.threadType.currentText() == PathThreadMilling.ObjectThreadMilling.ThreadTypeImperialInternal
|
||||
return (
|
||||
self.form.threadType.currentData()
|
||||
== PathThreadMilling.ObjectThreadMilling.ThreadTypeImperialInternal
|
||||
)
|
||||
|
||||
def _updateFromThreadType(self):
|
||||
|
||||
if self.form.threadType.currentText() == PathThreadMilling.ObjectThreadMilling.ThreadTypeCustom:
|
||||
if (
|
||||
self.form.threadType.currentData()
|
||||
== PathThreadMilling.ObjectThreadMilling.ThreadTypeCustom
|
||||
):
|
||||
self.form.threadName.setEnabled(False)
|
||||
self.form.threadFit.setEnabled(False)
|
||||
self.form.threadFitLabel.setEnabled(False)
|
||||
@@ -140,7 +186,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
self.form.threadTPI.setEnabled(False)
|
||||
self.form.threadTPILabel.setEnabled(False)
|
||||
self.form.threadTPI.setValue(0)
|
||||
fillThreads(self.form.threadName, 'metric-internal')
|
||||
fillThreads(self.form.threadName, "metric-internal")
|
||||
|
||||
if self._isThreadImperial():
|
||||
self.form.threadFit.setEnabled(True)
|
||||
@@ -150,24 +196,24 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
self.form.threadTPI.setEnabled(True)
|
||||
self.form.threadTPILabel.setEnabled(True)
|
||||
self.pitch.updateSpinBox(0)
|
||||
fillThreads(self.form.threadName, 'imperial-internal')
|
||||
fillThreads(self.form.threadName, "imperial-internal")
|
||||
|
||||
def _updateFromThreadName(self):
|
||||
thread = self.form.threadName.currentData()
|
||||
fit = float(self.form.threadFit.value()) / 100
|
||||
mamin = float(thread['dMajorMin'])
|
||||
mamax = float(thread['dMajorMax'])
|
||||
mamin = float(thread["dMajorMin"])
|
||||
mamax = float(thread["dMajorMax"])
|
||||
major = mamin + (mamax - mamin) * fit
|
||||
mimin = float(thread['dMinorMin'])
|
||||
mimax = float(thread['dMinorMax'])
|
||||
mimin = float(thread["dMinorMin"])
|
||||
mimax = float(thread["dMinorMax"])
|
||||
minor = mimin + (mimax - mimin) * fit
|
||||
|
||||
if self._isThreadMetric():
|
||||
pitch = float(thread['pitch'])
|
||||
pitch = float(thread["pitch"])
|
||||
self.pitch.updateSpinBox(pitch)
|
||||
|
||||
if self._isThreadImperial():
|
||||
tpi = int(thread['tpi'])
|
||||
tpi = int(thread["tpi"])
|
||||
self.form.threadTPI.setValue(tpi)
|
||||
minor = minor * 25.4
|
||||
major = major * 25.4
|
||||
@@ -178,7 +224,7 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
self.setDirty()
|
||||
|
||||
def getSignalsForUpdate(self, obj):
|
||||
'''getSignalsForUpdate(obj) ... return list of signals which cause the receiver to update the model'''
|
||||
"""getSignalsForUpdate(obj) ... return list of signals which cause the receiver to update the model"""
|
||||
signals = []
|
||||
|
||||
signals.append(self.form.threadMajor.editingFinished)
|
||||
@@ -200,12 +246,17 @@ class TaskPanelOpPage(PathCircularHoleBaseGui.TaskPanelOpPage):
|
||||
self.form.threadFit.valueChanged.connect(self._updateFromThreadName)
|
||||
|
||||
|
||||
Command = PathOpGui.SetupOperation('Thread Milling',
|
||||
PathThreadMilling.Create,
|
||||
TaskPanelOpPage,
|
||||
'Path_ThreadMilling',
|
||||
QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Thread Milling"),
|
||||
QtCore.QT_TRANSLATE_NOOP("PathThreadMilling", "Creates a Path Thread Milling operation from features of a base object"),
|
||||
PathThreadMilling.SetupProperties)
|
||||
Command = PathOpGui.SetupOperation(
|
||||
"ThreadMilling",
|
||||
PathThreadMilling.Create,
|
||||
TaskPanelOpPage,
|
||||
"Path_ThreadMilling",
|
||||
QT_TRANSLATE_NOOP("Path_ThreadMilling", "Thread Milling"),
|
||||
QT_TRANSLATE_NOOP(
|
||||
"Path_ThreadMilling",
|
||||
"Creates a Path Thread Milling operation from features of a base object",
|
||||
),
|
||||
PathThreadMilling.SetupProperties,
|
||||
)
|
||||
|
||||
FreeCAD.Console.PrintLog("Loading PathThreadMillingGui ... done\n")
|
||||
|
||||
Reference in New Issue
Block a user