diff --git a/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui b/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui
index 8de86b656a..cd24476351 100644
--- a/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui
+++ b/src/Mod/Path/Gui/Resources/panels/PageOpThreadMillingEdit.ui
@@ -30,18 +30,8 @@
-
- 1
+ -1
-
-
-
- Left Hand
-
-
- -
-
- Right Hand
-
-
-
@@ -52,23 +42,7 @@
-
-
-
-
-
- Custom
-
-
- -
-
- Metric - internal
-
-
- -
-
- SAE - internal
-
-
-
+
-
@@ -208,18 +182,7 @@
-
-
-
-
-
- Climb
-
-
- -
-
- Conventional
-
-
-
+
-
@@ -236,7 +199,7 @@
Gui::QuantitySpinBox
- QDoubleSpinBox
+ QWidget
diff --git a/src/Mod/Path/InitGui.py b/src/Mod/Path/InitGui.py
index 820133a723..32f44f5dfb 100644
--- a/src/Mod/Path/InitGui.py
+++ b/src/Mod/Path/InitGui.py
@@ -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():
diff --git a/src/Mod/Path/PathScripts/PathSelection.py b/src/Mod/Path/PathScripts/PathSelection.py
index 2c640aaca2..ef561f6361 100644
--- a/src/Mod/Path/PathScripts/PathSelection.py
+++ b/src/Mod/Path/PathScripts/PathSelection.py
@@ -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
diff --git a/src/Mod/Path/PathScripts/PathThreadMilling.py b/src/Mod/Path/PathScripts/PathThreadMilling.py
index 5d89c285b2..3dc5eb02c3 100644
--- a/src/Mod/Path/PathScripts/PathThreadMilling.py
+++ b/src/Mod/Path/PathScripts/PathThreadMilling.py
@@ -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
-
diff --git a/src/Mod/Path/PathScripts/PathThreadMillingGui.py b/src/Mod/Path/PathScripts/PathThreadMillingGui.py
index ff3bcd904f..1514f951e1 100644
--- a/src/Mod/Path/PathScripts/PathThreadMillingGui.py
+++ b/src/Mod/Path/PathScripts/PathThreadMillingGui.py
@@ -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")