diff --git a/src/Mod/CAM/Gui/Resources/panels/DressUpLeadInOutEdit.ui b/src/Mod/CAM/Gui/Resources/panels/DressUpLeadInOutEdit.ui index baab2f8dab..c713aeb95d 100644 --- a/src/Mod/CAM/Gui/Resources/panels/DressUpLeadInOutEdit.ui +++ b/src/Mod/CAM/Gui/Resources/panels/DressUpLeadInOutEdit.ui @@ -57,27 +57,21 @@ - Radius/length (% tool radius) + Radius/length - + - Length of the Lead-in, as a percentage of tool radius - - - 0 + Length of the Lead-in - 1.000000000000000 + 0.000000000000000 999999.000000000000000 - - 10.000000000000000 - @@ -175,27 +169,21 @@ - Radius/length (% tool radius) + Radius/length - + - Length of the Lead-out, as a percentage of tool radius - - - 0 + Length of the Lead-out - 1.000000000000000 + 0.000000000000000 999999.000000000000000 - - 10.000000000000000 - diff --git a/src/Mod/CAM/Path/Dressup/Gui/LeadInOut.py b/src/Mod/CAM/Path/Dressup/Gui/LeadInOut.py index 769bb34c1f..baf7b6de06 100644 --- a/src/Mod/CAM/Path/Dressup/Gui/LeadInOut.py +++ b/src/Mod/CAM/Path/Dressup/Gui/LeadInOut.py @@ -27,6 +27,7 @@ import Path import Path.Base.Language as PathLanguage import Path.Dressup.Utils as PathDressup import PathScripts.PathUtils as PathUtils +from Path.Base.Util import toolControllerForOp import copy import math @@ -44,7 +45,7 @@ if False: else: Path.Log.setLevel(Path.Log.Level.INFO, Path.Log.thisModule()) -lead_styles = [ +lead_styles = ( # common options first QT_TRANSLATE_NOOP("CAM_DressupLeadInOut", "Arc"), QT_TRANSLATE_NOOP("CAM_DressupLeadInOut", "Line"), @@ -56,7 +57,7 @@ lead_styles = [ QT_TRANSLATE_NOOP("CAM_DressupLeadInOut", "LineZ"), QT_TRANSLATE_NOOP("CAM_DressupLeadInOut", "No Retract"), QT_TRANSLATE_NOOP("CAM_DressupLeadInOut", "Vertical"), -] +) class ObjectDressup: @@ -121,14 +122,14 @@ class ObjectDressup: QT_TRANSLATE_NOOP("App::Property", "Angle of the Lead-Out (1..90)"), ) obj.addProperty( - "App::PropertyInteger", - "PercentageRadiusIn", + "App::PropertyLength", + "RadiusIn", "Path Lead-in", QT_TRANSLATE_NOOP("App::Property", "Determine length of the Lead-In"), ) obj.addProperty( - "App::PropertyInteger", - "PercentageRadiusOut", + "App::PropertyLength", + "RadiusOut", "Path Lead-out", QT_TRANSLATE_NOOP("App::Property", "Determine length of the Lead-Out"), ) @@ -171,12 +172,30 @@ class ObjectDressup: obj.AngleOut = 45 obj.InvertIn = False obj.InvertOut = False - obj.PercentageRadiusIn = 150 - obj.PercentageRadiusOut = 150 obj.RapidPlunge = False obj.StyleIn = "Arc" obj.StyleOut = "Arc" + baseWithTC = self.getBaseWithTC(obj) + if baseWithTC and baseWithTC.ToolController: + expr = f"{baseWithTC.Name}.ToolController.Tool.Diameter.Value/2*1.5" + obj.setExpression("RadiusIn", expr) + obj.setExpression("RadiusOut", expr) + else: + obj.RadiusIn = 10 + obj.RadiusOut = 10 + + def getBaseWithTC(self, obj): + if hasattr(obj, "ToolController"): + return obj + if not hasattr(obj, "Base"): + return None + if isinstance(obj.Base, list) and obj.Base and obj.Base[0].isDerivedFrom("Path::Feature"): + return self.getBaseWithTC(obj.Base[0]) + elif not isinstance(obj.Base, list) and obj.Base.isDerivedFrom("Path::Feature"): + return self.getBaseWithTC(obj.Base) + return None + def execute(self, obj): if not obj.Base: obj.Path = Path.Path() @@ -188,10 +207,10 @@ class ObjectDressup: obj.Path = Path.Path() return - if obj.PercentageRadiusIn < 1: - obj.PercentageRadiusIn = 1 - if obj.PercentageRadiusOut < 1: - obj.PercentageRadiusOut = 1 + if obj.RadiusIn <= 0: + obj.RadiusIn = 1 + if obj.RadiusOut <= 0: + obj.RadiusOut = 1 nonZeroAngleStyles = ("Arc", "Arc3d", "ArcZ", "Helix", "LineZ") limit_angle_in = 1 if obj.StyleIn in nonZeroAngleStyles else 0 @@ -207,15 +226,30 @@ class ObjectDressup: obj.AngleOut = limit_angle_out hideModes = { - "Angle": ["No Retract", "Vertical"], - "Invert": ["No Retract", "ArcZ", "LineZ", "Vertical"], - "Offset": ["No Retract"], - "PercentageRadius": ["No Retract", "Vertical"], + "Angle": ("No Retract", "Vertical"), + "Invert": ("No Retract", "ArcZ", "LineZ", "Vertical"), + "Offset": ("No Retract"), + "Radius": ("No Retract", "Vertical"), } for k, v in hideModes.items(): obj.setEditorMode(k + "In", 2 if obj.StyleIn in v else 0) obj.setEditorMode(k + "Out", 2 if obj.StyleOut in v else 0) + self.baseOp = PathDressup.baseOp(obj.Base) + self.toolController = toolControllerForOp(obj.Base) + if not self.toolController: + obj.Path = Path.Path() + Path.Log.warning( + translate( + "CAM_DressupLeadInOut", "Tool controller not selected for base operation: %s" + ) + % obj.Base.Label + ) + return + + self.horizFeed = self.toolController.HorizFeed.Value + self.vertFeed = self.toolController.VertFeed.Value + obj.Path = self.generateLeadInOutCurve(obj) def onDocumentRestored(self, obj): @@ -267,7 +301,7 @@ class ObjectDressup: if styleOn == "Arc": obj.StyleIn = "Arc" obj.AngleIn = 90 - elif styleOn in ["Perpendicular", "Tangent"]: + elif styleOn in ("Perpendicular", "Tangent"): obj.StyleIn = "Line" obj.AngleIn = 90 if styleOn == "Perpendicular" else 0 @@ -275,35 +309,49 @@ class ObjectDressup: if styleOff == "Arc": obj.StyleOut = "Arc" obj.AngleOut = 90 - elif styleOff in ["Perpendicular", "Tangent"]: + elif styleOff in ("Perpendicular", "Tangent"): obj.StyleOut = "Line" obj.AngleOut = 90 if styleOff == "Perpendicular" else 0 - toolRadius = PathDressup.toolController(obj.Base).Tool.Diameter.Value / 2 - if hasattr(obj, "Length") or hasattr(obj, "LengthIn"): - oldLength = obj.LengthIn if hasattr(obj, "LengthIn") else obj.Length - for prop in ["Length", "LengthIn"]: - if hasattr(obj, prop): - obj.removeProperty(prop) + for prop in ("Length", "LengthIn"): + if hasattr(obj, prop): + obj.renameProperty(prop, "RadiusIn") + break - # Replace Length by PercentageRadiusIn - obj.addProperty( - "App::PropertyInteger", - "PercentageRadiusIn", - "Path Lead-in", - QT_TRANSLATE_NOOP("App::Property", "Determine length of the Lead-In"), - ) - obj.PercentageRadiusIn = int(oldLength / toolRadius * 100) if hasattr(obj, "LengthOut"): - # Replace LengthOut by PercentageRadiusOut - obj.addProperty( - "App::PropertyInteger", - "PercentageRadiusOut", - "Path Lead-out", - QT_TRANSLATE_NOOP("App::Property", "Determine length of the Lead-Out"), - ) - obj.PercentageRadiusOut = int(obj.LengthOut / toolRadius * 100) - obj.removeProperty("LengthOut") + obj.renameProperty("LengthOut", "RadiusOut") + + if hasattr(obj, "PercentageRadiusIn") or hasattr(obj, "PercentageRadiusOut"): + baseWithTC = self.getBaseWithTC(obj) + if hasattr(obj, "PercentageRadiusIn"): + obj.addProperty( + "App::PropertyLength", + "RadiusIn", + "Path Lead-in", + QT_TRANSLATE_NOOP("App::Property", "Determine length of the Lead-In"), + ) + if baseWithTC and baseWithTC.ToolController: + valIn = obj.PercentageRadiusIn / 100 + exprIn = f"{baseWithTC.Name}.ToolController.Tool.Diameter.Value/2*{valIn}" + obj.setExpression("RadiusIn", exprIn) + else: + obj.RadiusIn = 10 + obj.removeProperty("PercentageRadiusIn") + + if hasattr(obj, "PercentageRadiusOut"): + obj.addProperty( + "App::PropertyLength", + "RadiusOut", + "Path Lead-out", + QT_TRANSLATE_NOOP("App::Property", "Determine length of the Lead-Out"), + ) + if baseWithTC and baseWithTC.ToolController: + valOut = obj.PercentageRadiusOut / 100 + exprOut = f"{baseWithTC.Name}.ToolController.Tool.Diameter.Value/2*{valOut}" + obj.setExpression("RadiusOut", exprOut) + else: + obj.RadiusOut = 10 + obj.removeProperty("PercentageRadiusOut") # The new features do not have a good analog for ExtendLeadIn/Out, so these old values will be ignored if hasattr(obj, "ExtendLeadIn"): @@ -361,9 +409,8 @@ class ObjectDressup: # Get direction for lead-in/lead-out in XY plane def getLeadDir(self, obj, invert=False): output = math.pi / 2 - op = PathDressup.baseOp(obj.Base) - side = op.Side if hasattr(op, "Side") else "Inside" - direction = op.Direction if hasattr(op, "Direction") else "CCW" + side = self.baseOp.Side if hasattr(self.baseOp, "Side") else "Inside" + direction = self.baseOp.Direction if hasattr(self.baseOp, "Direction") else "CCW" if (side == "Inside" and direction == "CW") or (side == "Outside" and direction == "CCW"): output = -output if invert: @@ -374,8 +421,7 @@ class ObjectDressup: # Get direction of original path def getPathDir(self, obj): # only CW or CCW is matter - op = PathDressup.baseOp(obj.Base) - direction = op.Direction if hasattr(op, "Direction") else "CCW" + direction = self.baseOp.Direction if hasattr(self.baseOp, "Direction") else "CCW" output = math.pi / 2 if direction == "CW": output = -output @@ -384,8 +430,6 @@ class ObjectDressup: # Create safety movements to start point def getTravelStart(self, obj, pos, first, outInstrPrev): - op = PathDressup.baseOp(obj.Base) - vertfeed = PathDressup.toolController(obj.Base).VertFeed.Value commands = [] posPrev = outInstrPrev.positionEnd() if outInstrPrev else App.Vector() posPrevXY = App.Vector(posPrev.x, posPrev.y, 0) @@ -394,7 +438,9 @@ class ObjectDressup: if first or (distance > obj.RetractThreshold): # move to clearance height - commands.append(PathLanguage.MoveStraight(None, "G00", {"Z": op.ClearanceHeight.Value})) + commands.append( + PathLanguage.MoveStraight(None, "G00", {"Z": self.baseOp.ClearanceHeight.Value}) + ) # move to mill position at clearance height commands.append(PathLanguage.MoveStraight(None, "G00", {"X": pos.x, "Y": pos.y})) @@ -405,8 +451,12 @@ class ObjectDressup: commands.append(PathLanguage.MoveStraight(None, "G00", {"Z": pos.z})) else: # move to mill position in two steps - commands.append(PathLanguage.MoveStraight(None, "G00", {"Z": op.SafeHeight.Value})) - commands.append(PathLanguage.MoveStraight(None, "G01", {"Z": pos.z, "F": vertfeed})) + commands.append( + PathLanguage.MoveStraight(None, "G00", {"Z": self.baseOp.SafeHeight.Value}) + ) + commands.append( + PathLanguage.MoveStraight(None, "G01", {"Z": pos.z, "F": self.vertFeed}) + ) else: # move to next mill position by short path @@ -417,7 +467,7 @@ class ObjectDressup: else: commands.append( PathLanguage.MoveStraight( - None, "G01", {"X": pos.x, "Y": pos.y, "Z": pos.z, "F": vertfeed} + None, "G01", {"X": pos.x, "Y": pos.y, "Z": pos.z, "F": self.vertFeed} ) ) @@ -426,8 +476,7 @@ class ObjectDressup: # Create commands with movements to clearance height def getTravelEnd(self, obj): commands = [] - op = PathDressup.baseOp(obj.Base) - z = op.ClearanceHeight.Value + z = self.baseOp.ClearanceHeight.Value commands.append(PathLanguage.MoveStraight(None, "G00", {"Z": z})) return commands @@ -438,8 +487,14 @@ class ObjectDressup: # Create arc in XY plane with automatic detection G2|G3 def createArcMove(self, obj, begin, end, offset, invert=False): - horizfeed = PathDressup.toolController(obj.Base).HorizFeed.Value - param = {"X": end.x, "Y": end.y, "Z": end.z, "I": offset.x, "J": offset.y, "F": horizfeed} + param = { + "X": end.x, + "Y": end.y, + "Z": end.z, + "I": offset.x, + "J": offset.y, + "F": self.horizFeed, + } if self.getLeadDir(obj, invert) > 0: command = PathLanguage.MoveArcCCW(begin, "G3", param) else: @@ -449,8 +504,7 @@ class ObjectDressup: # Create arc in XY plane with manually set G2|G3 def createArcMoveN(self, obj, begin, end, offset, cmdName): - horizfeed = PathDressup.toolController(obj.Base).HorizFeed.Value - param = {"X": end.x, "Y": end.y, "I": offset.x, "J": offset.y, "F": horizfeed} + param = {"X": end.x, "Y": end.y, "I": offset.x, "J": offset.y, "F": self.horizFeed} if cmdName == "G2": command = PathLanguage.MoveArcCW(begin, cmdName, param) else: @@ -460,8 +514,7 @@ class ObjectDressup: # Create line movement G1 def createStraightMove(self, obj, begin, end): - horizfeed = PathDressup.toolController(obj.Base).HorizFeed.Value - param = {"X": end.x, "Y": end.y, "Z": end.z, "F": horizfeed} + param = {"X": end.x, "Y": end.y, "Z": end.z, "F": self.horizFeed} command = PathLanguage.MoveStraight(begin, "G1", param) return command @@ -485,7 +538,6 @@ class ObjectDressup: # Create vertical arc with move Down by line segments def createArcZMoveDown(self, obj, begin, end, radius): commands = [] - horizfeed = PathDressup.toolController(obj.Base).HorizFeed.Value angle = math.acos((radius - begin.z + end.z) / radius) # start angle stepAngle = self.getStepAngleArcZ(obj, radius) iters = math.ceil(angle / stepAngle) @@ -503,7 +555,7 @@ class ObjectDressup: else: # exclude error of calculations for the last iteration iterEnd = copy.copy(end) - param = {"X": iterEnd.x, "Y": iterEnd.y, "Z": iterEnd.z, "F": horizfeed} + param = {"X": iterEnd.x, "Y": iterEnd.y, "Z": iterEnd.z, "F": self.horizFeed} commands.append(PathLanguage.MoveStraight(iterBegin, "G1", param)) iterBegin = copy.copy(iterEnd) iter += 1 @@ -513,7 +565,6 @@ class ObjectDressup: # Create vertical arc with move Up by line segments def createArcZMoveUp(self, obj, begin, end, radius): commands = [] - horizfeed = PathDressup.toolController(obj.Base).HorizFeed.Value angleMax = math.acos((radius - end.z + begin.z) / radius) # finish angle stepAngle = self.getStepAngleArcZ(obj, radius) iters = math.ceil(angleMax / stepAngle) @@ -532,7 +583,7 @@ class ObjectDressup: else: # exclude the error of calculations of the last point iterEnd = copy.copy(end) - param = {"X": iterEnd.x, "Y": iterEnd.y, "Z": iterEnd.z, "F": horizfeed} + param = {"X": iterEnd.x, "Y": iterEnd.y, "Z": iterEnd.z, "F": self.horizFeed} commands.append(PathLanguage.MoveStraight(iterBegin, "G1", param)) iterBegin = copy.copy(iterEnd) iter += 1 @@ -552,10 +603,9 @@ class ObjectDressup: begin = move.positionBegin() beginZ = move.positionBegin().z # do not change this variable below - if obj.StyleIn not in ["No Retract", "Vertical"]: - toolRadius = PathDressup.toolController(obj.Base).Tool.Diameter.Value / 2 + if obj.StyleIn not in ("No Retract", "Vertical"): angleIn = math.radians(obj.AngleIn.Value) - length = obj.PercentageRadiusIn * toolRadius / 100 + length = obj.RadiusIn.Value angleTangent = move.anglesOfTangents()[0] normalMax = ( self.angleToVector(angleTangent + self.getLeadDir(obj, obj.InvertIn)) * length @@ -566,7 +616,7 @@ class ObjectDressup: # prepend "Arc" style lead-in - arc in XY # Arc3d the same as Arc, but increased Z start point - if obj.StyleIn in ["Arc", "Arc3d", "Helix"]: + if obj.StyleIn in ("Arc", "Arc3d", "Helix"): # tangent and normal vectors in XY plane arcRadius = length tangentLength = math.sin(angleIn) * arcRadius @@ -583,7 +633,7 @@ class ObjectDressup: # prepend "Line" style lead-in - line in XY # Line3d the same as Line, but increased Z start point - elif obj.StyleIn in ["Line", "Line3d"]: + elif obj.StyleIn in ("Line", "Line3d"): # tangent and normal vectors in XY plane tangentLength = math.cos(angleIn) * length normalLength = math.sin(angleIn) * length @@ -600,8 +650,7 @@ class ObjectDressup: elif obj.StyleIn == "LineZ": # tangent vector in XY plane # normal vector is vertical - op = PathDressup.baseOp(obj.Base) - normalLengthMax = op.SafeHeight.Value - begin.z + normalLengthMax = self.baseOp.SafeHeight.Value - begin.z normalLength = math.sin(angleIn) * length # do not exceed Normal vector max length normalLength = min(normalLength, normalLengthMax) @@ -616,9 +665,8 @@ class ObjectDressup: elif obj.StyleIn == "ArcZ": # tangent vector in XY plane # normal vector is vertical - op = PathDressup.baseOp(obj.Base) arcRadius = length - normalLengthMax = op.SafeHeight.Value - begin.z + normalLengthMax = self.baseOp.SafeHeight.Value - begin.z normalLength = arcRadius * (1 - math.cos(angleIn)) if normalLength > normalLengthMax: # do not exceed Normal vector max length @@ -634,13 +682,12 @@ class ObjectDressup: # replace 'begin' position by first lead-in command begin = lead[0].positionBegin() - if obj.StyleIn in ["Arc3d", "Line3d"]: + if obj.StyleIn in ("Arc3d", "Line3d"): # up Z start point for Arc3d and Line3d if inInstrPrev and inInstrPrev.z() > begin.z: begin.z = inInstrPrev.z() else: - op = PathDressup.baseOp(obj.Base) - begin.z = op.StartDepth.Value + begin.z = self.baseOp.StartDepth.Value lead[0].setPositionBegin(begin) elif obj.StyleIn == "Helix": @@ -652,8 +699,7 @@ class ObjectDressup: halfStepZ = (posPrevZ - beginZ) / 2 begin.z += halfStepZ else: - op = PathDressup.baseOp(obj.Base) - begin.z = op.StartDepth.Value + begin.z = self.baseOp.StartDepth.Value if obj.StyleOut == "Helix" and outInstrPrev: """change Z for previous helix lead-out @@ -671,8 +717,7 @@ class ObjectDressup: travelToStart = self.getTravelStart(obj, begin, first, outInstrPrev) else: # exclude any lead-in commands - horizfeed = PathDressup.toolController(obj.Base).HorizFeed.Value - param = {"X": begin.x, "Y": begin.y, "Z": begin.z, "F": horizfeed} + param = {"X": begin.x, "Y": begin.y, "Z": begin.z, "F": self.horizFeed} travelToStart = [PathLanguage.MoveStraight(None, "G01", param)] lead = travelToStart + lead @@ -691,10 +736,9 @@ class ObjectDressup: lead = [] end = move.positionEnd() - if obj.StyleOut not in ["No Retract", "Vertical"]: - toolRadius = PathDressup.toolController(obj.Base).Tool.Diameter.Value / 2 + if obj.StyleOut not in ("No Retract", "Vertical"): angleOut = math.radians(obj.AngleOut.Value) - length = obj.PercentageRadiusOut * toolRadius / 100 + length = obj.RadiusOut.Value angleTangent = move.anglesOfTangents()[1] normalMax = ( self.angleToVector(angleTangent + self.getLeadDir(obj, obj.InvertOut)) * length @@ -705,7 +749,7 @@ class ObjectDressup: # append "Arc" style lead-out - arc in XY # Arc3d the same as Arc, but increased Z start point - if obj.StyleOut in ["Arc", "Arc3d", "Helix"]: + if obj.StyleOut in ("Arc", "Arc3d", "Helix"): # tangent and normal vectors in XY plane arcRadius = length tangentLength = math.sin(angleOut) * arcRadius @@ -720,7 +764,7 @@ class ObjectDressup: # append "Line" style lead-out # Line3d the same as Line, but increased Z start point - elif obj.StyleOut in ["Line", "Line3d"]: + elif obj.StyleOut in ("Line", "Line3d"): # tangent and normal vectors in XY plane tangentLength = math.cos(angleOut) * length normalLength = math.sin(angleOut) * length @@ -737,8 +781,7 @@ class ObjectDressup: elif obj.StyleOut == "LineZ": # tangent vector in XY plane # normal vector is vertical - op = PathDressup.baseOp(obj.Base) - normalLengthMax = op.StartDepth.Value - end.z + normalLengthMax = self.baseOp.StartDepth.Value - end.z normalLength = math.sin(angleOut) * length # do not exceed Normal vector max length normalLength = min(normalLength, normalLengthMax) @@ -753,9 +796,8 @@ class ObjectDressup: elif obj.StyleOut == "ArcZ": # tangent vector in XY plane # normal vector is vertical - op = PathDressup.baseOp(obj.Base) arcRadius = length - normalLengthMax = op.SafeHeight.Value - end.z + normalLengthMax = self.baseOp.SafeHeight.Value - end.z normalLength = arcRadius * (1 - math.cos(angleOut)) if normalLength > normalLengthMax: # do not exceed Normal vector max length @@ -768,13 +810,12 @@ class ObjectDressup: arcEnd = end + tangent + normal lead.extend(self.createArcZMoveUp(obj, end, arcEnd, arcRadius)) - if obj.StyleOut in ["Arc3d", "Line3d"]: + if obj.StyleOut in ("Arc3d", "Line3d"): # Up Z end point for Arc3d and Line3d - op = PathDressup.baseOp(obj.Base) if outInstrPrev and outInstrPrev.positionBegin().z > end.z: lead[-1].param["Z"] = outInstrPrev.positionBegin().z else: - lead[-1].param["Z"] = op.StartDepth.Value + lead[-1].param["Z"] = self.baseOp.StartDepth.Value # append travel moves to clearance height after finish all profiles if last and obj.StyleOut != "No Retract": @@ -1004,10 +1045,10 @@ class ObjectDressup: commands.append(instr) else: moveDir = self.getMoveDir(instr) - if not obj.LeadIn and (moveDir in ["Down", "Hor"] or first): + if not obj.LeadIn and (moveDir in ("Down", "Hor") or first): # keep original Lead-in movements commands.append(instr) - elif not obj.LeadOut and moveDir in ["Up"] and not first: + elif not obj.LeadOut and moveDir == "Up" and not first: # keep original Lead-out movements commands.append(instr) # skip travel and plunge moves if LeadInOut will be process @@ -1107,8 +1148,8 @@ class TaskDressupLeadInOut(SimpleEditPanel): def setupUi(self): self.connectWidget("InvertIn", self.form.chkInvertDirectionIn) self.connectWidget("InvertOut", self.form.chkInvertDirectionOut) - self.connectWidget("PercentageRadiusIn", self.form.dspPercentageRadiusIn) - self.connectWidget("PercentageRadiusOut", self.form.dspPercentageRadiusOut) + self.connectWidget("RadiusIn", self.form.dspRadiusIn) + self.connectWidget("RadiusOut", self.form.dspRadiusOut) self.connectWidget("StyleIn", self.form.cboStyleIn) self.connectWidget("StyleOut", self.form.cboStyleOut) self.connectWidget("AngleIn", self.form.dspAngleIn)