From 98d4042b299f93f4380d6864651c3f18ee48192d Mon Sep 17 00:00:00 2001 From: sliptonic Date: Sat, 22 Jan 2022 14:20:53 -0600 Subject: [PATCH] Threadmilling translation cleanup --- .../panels/PageOpThreadMillingEdit.ui | 45 +-- src/Mod/Path/InitGui.py | 2 +- src/Mod/Path/PathScripts/PathSelection.py | 2 +- src/Mod/Path/PathScripts/PathThreadMilling.py | 329 +++++++++++++----- .../Path/PathScripts/PathThreadMillingGui.py | 151 +++++--- 5 files changed, 350 insertions(+), 179 deletions(-) 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
Gui/QuantitySpinBox.h
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")