From ea6a30efba28c2db7bf19c213b50f85242cc94ea Mon Sep 17 00:00:00 2001 From: Roy-043 Date: Fri, 25 Apr 2025 19:48:07 +0200 Subject: [PATCH] Draft: Add Global mode to ShapeString task panel This PR adds the Global mode option to the ShapeString task panel. Additionally: * Rounding of coordinates caused by reading values from the task panel is avoided. * Improved task panel layout: Height input above String input. * Some code reformatting. --- src/Mod/Draft/Resources/ui/TaskShapeString.ui | 85 ++---- .../Draft/drafttaskpanels/task_shapestring.py | 252 +++++++++++------- 2 files changed, 181 insertions(+), 156 deletions(-) diff --git a/src/Mod/Draft/Resources/ui/TaskShapeString.ui b/src/Mod/Draft/Resources/ui/TaskShapeString.ui index 976588c759..6657c922c5 100644 --- a/src/Mod/Draft/Resources/ui/TaskShapeString.ui +++ b/src/Mod/Draft/Resources/ui/TaskShapeString.ui @@ -81,7 +81,7 @@ - + Enter coordinates or select point with mouse. @@ -91,24 +91,18 @@ - - - - - - - - Qt::Horizontal + + + + Coordinates relative to global coordinate system. +Uncheck to use working plane coordinate system - - - 40 - 20 - + + Global - + - + Reset 3D point selection @@ -121,36 +115,14 @@ - - - - - - - - String - - - - - - - Text to be made into ShapeString - - - - - - - - + Height - + Height of the result @@ -168,8 +140,22 @@ - - + + + + + + String + + + + + + + Text to be made into ShapeString + + + @@ -186,20 +172,7 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - - + diff --git a/src/Mod/Draft/drafttaskpanels/task_shapestring.py b/src/Mod/Draft/drafttaskpanels/task_shapestring.py index 26d6616668..a7c29f8868 100644 --- a/src/Mod/Draft/drafttaskpanels/task_shapestring.py +++ b/src/Mod/Draft/drafttaskpanels/task_shapestring.py @@ -1,6 +1,7 @@ # *************************************************************************** -# * (c) 2009 Yorik van Havre * -# * (c) 2020 Eliud Cabrera Castillo * +# * Copyright (c) 2009 Yorik van Havre * +# * Copyright (c) 2020 Eliud Cabrera Castillo * +# * Copyright (c) 2025 FreeCAD Project Association * # * * # * This file is part of the FreeCAD CAx development system. * # * * @@ -34,9 +35,9 @@ import PySide.QtGui as QtGui import FreeCAD as App import FreeCADGui as Gui import Draft_rc +import WorkingPlane from draftguitools import gui_tool_utils -from draftutils.messages import _err -from draftutils.params import get_param +from draftutils import params from draftutils.translate import translate from DraftVecUtils import toString @@ -47,7 +48,7 @@ True if Draft_rc.__name__ else False class ShapeStringTaskPanel: """Base class for Draft_ShapeString task panel.""" - def __init__(self, point=App.Vector(0,0,0), size=10, string="", font=""): + def __init__(self, point=None, size=10, string="", font=""): self.form = Gui.PySideUic.loadUi(":/ui/TaskShapeString.ui") self.form.setObjectName("ShapeStringTaskPanel") @@ -55,39 +56,44 @@ class ShapeStringTaskPanel: self.form.setWindowIcon(QtGui.QIcon(":/icons/Draft_ShapeString.svg")) unit_length = App.Units.Quantity(0.0, App.Units.Length).getUserPreferred()[2] - self.form.sbX.setProperty('rawValue', point.x) - self.form.sbX.setProperty('unit', unit_length) - self.form.sbY.setProperty('rawValue', point.y) - self.form.sbY.setProperty('unit', unit_length) - self.form.sbZ.setProperty('rawValue', point.z) - self.form.sbZ.setProperty('unit', unit_length) - self.form.sbHeight.setProperty('rawValue', size) - self.form.sbHeight.setProperty('unit', unit_length) + self.form.sbX.setProperty("unit", unit_length) + self.form.sbY.setProperty("unit", unit_length) + self.form.sbZ.setProperty("unit", unit_length) + self.form.sbHeight.setProperty("unit", unit_length) - self.stringText = string if string else translate("draft", "Default") - self.form.leString.setText(self.stringText) - self.platWinDialog("Overwrite") - self.fileSpec = font if font else get_param("FontFile") - self.form.fcFontFile.setFileName(self.fileSpec) - self.point = point + self.global_mode = params.get_param("GlobalMode") + self.form.cbGlobalMode.setChecked(self.global_mode) + self.change_coord_labels() + + self.wp = WorkingPlane.get_working_plane() + if point is not None: + self.point = point + elif self.global_mode: + self.point = App.Vector() + else: + self.point = self.wp.position + self.display_point(self.point) self.pointPicked = False + + self.form.sbHeight.setProperty("rawValue", size) + self.string = string if string else translate("draft", "Default") + self.form.leString.setText(self.string) + self.platform_win_dialog("Overwrite") + self.font_file = font if font else params.get_param("FontFile") + self.form.fcFontFile.setFileName(self.font_file) + # Prevent cyclic processing of point values: + self.display_point_active = False # Default for the "DontUseNativeFontDialog" preference: self.font_dialog_pref = False - # Dummy attribute used by gui_tools_utils.getPoint in action method + # Dummy attribute used by gui_tool_utils.getPoint in action method self.node = None - QtCore.QObject.connect(self.form.fcFontFile, QtCore.SIGNAL("fileNameSelected(const QString&)"), self.fileSelect) - QtCore.QObject.connect(self.form.pbReset, QtCore.SIGNAL("clicked()"), self.resetPoint) - - def fileSelect(self, fn): - """Assign the selected file.""" - self.fileSpec = fn - - def resetPoint(self): - """Reset the selected point.""" - self.pointPicked = False - origin = App.Vector(0.0, 0.0, 0.0) - self.setPoint(origin) + self.form.sbX.valueChanged.connect(self.set_point_x) + self.form.sbY.valueChanged.connect(self.set_point_y) + self.form.sbZ.valueChanged.connect(self.set_point_z) + self.form.cbGlobalMode.stateChanged.connect(self.set_global_mode) + self.form.fcFontFile.fileNameSelected.connect(self.set_file) + self.form.pbReset.clicked.connect(self.reset_point) def action(self, arg): """scene event handler""" @@ -95,36 +101,95 @@ class ShapeStringTaskPanel: if arg["Key"] == "ESCAPE": self.reject() elif arg["Type"] == "SoLocation2Event": # mouse movement detection - self.point,ctrlPoint,info = gui_tool_utils.getPoint(self, arg, noTracker=True) if not self.pointPicked: - self.setPoint(self.point) + self.point, ctrlPoint, info = gui_tool_utils.getPoint(self, arg, noTracker=True) + self.display_point(self.point) elif arg["Type"] == "SoMouseButtonEvent": if (arg["State"] == "DOWN") and (arg["Button"] == "BUTTON1"): - self.setPoint(self.point) + self.display_point(self.point) self.pointPicked = True - def setPoint(self, point): - """Assign the selected point.""" - self.form.sbX.setProperty('rawValue', point.x) - self.form.sbY.setProperty('rawValue', point.y) - self.form.sbZ.setProperty('rawValue', point.z) + def change_coord_labels(self): + if self.global_mode: + self.form.labelX.setText(translate("draft", "Global {}").format("X")) + self.form.labelY.setText(translate("draft", "Global {}").format("Y")) + self.form.labelZ.setText(translate("draft", "Global {}").format("Z")) + else: + self.form.labelX.setText(translate("draft", "Local {}").format("X")) + self.form.labelY.setText(translate("draft", "Local {}").format("Y")) + self.form.labelZ.setText(translate("draft", "Local {}").format("Z")) + def display_point(self, point): + """Display the selected point.""" + self.display_point_active = True + if not self.global_mode: + point = self.wp.get_local_coords(point) + self.form.sbX.setProperty("rawValue", point.x) + self.form.sbY.setProperty("rawValue", point.y) + self.form.sbZ.setProperty("rawValue", point.z) + self.display_point_active = False - def platWinDialog(self, flag): + def escape_string(self, string): + return string.replace("\\", "\\\\").replace("\"", "\\\"") + + def platform_win_dialog(self, flag): """Handle the type of dialog depending on the platform.""" ParamGroup = App.ParamGet("User parameter:BaseApp/Preferences/Dialog") if flag == "Overwrite": if "DontUseNativeFontDialog" not in ParamGroup.GetBools(): # initialize nonexisting one ParamGroup.SetBool("DontUseNativeFontDialog", True) - param = ParamGroup.GetBool("DontUseNativeFontDialog") self.font_dialog_pref = ParamGroup.GetBool("DontUseNativeDialog") ParamGroup.SetBool("DontUseNativeDialog", param) - elif flag == "Restore": ParamGroup.SetBool("DontUseNativeDialog", self.font_dialog_pref) + def reset_point(self): + """Reset the selected point and display new point in the task panel.""" + if self.global_mode: + self.point = App.Vector() + else: + self.point = self.wp.position + self.pointPicked = False + self.display_point(self.point) + + def set_file(self, val): + """Assign the selected font file.""" + self.font_file = val + + def set_global_mode(self, val): + self.global_mode = bool(val) + params.set_param("GlobalMode", self.global_mode) + self.change_coord_labels() + self.display_point(self.point) + + def set_point_coord(self, coord, val): + """Change self.point based on X, Y or Z value entered in the task panel. + + coord should be "x", "y" or "z". + """ + if self.display_point_active: + return + if self.global_mode: + point = self.point + else: + point = self.wp.get_local_coords(self.point) + setattr(point, coord, App.Units.Quantity(val).Value) + if self.global_mode: + self.point = point + else: + self.point = self.wp.get_global_coords(point) + + def set_point_x(self, val): + self.set_point_coord("x", val) + + def set_point_y(self, val): + self.set_point_coord("y", val) + + def set_point_z(self, val): + self.set_point_coord("z", val) + class ShapeStringTaskPanelCmd(ShapeStringTaskPanel): """Task panel for Draft_ShapeString.""" @@ -135,85 +200,66 @@ class ShapeStringTaskPanelCmd(ShapeStringTaskPanel): def accept(self): """Execute when clicking the OK button.""" - self.createObject() + self.create_object() self.reject() - return True + def create_object(self): + """Create object in the current document.""" + string = self.escape_string(self.form.leString.text()) + size = App.Units.Quantity(self.form.sbHeight.text()).Value + + Gui.addModule("Draft") + Gui.addModule("WorkingPlane") + cmd = "Draft.make_shapestring(" + cmd += "String=\"" + string + "\", " + cmd += "FontFile=\"" + self.font_file + "\", " + cmd += "Size=" + str(size) + ", " + cmd += "Tracking=0.0" + cmd += ")" + self.sourceCmd.commit( + translate("draft", "Create ShapeString"), + ["ss = " + cmd, + "pl = FreeCAD.Placement()", + "pl.Base = " + toString(self.point), + "pl.Rotation = WorkingPlane.get_working_plane().get_placement().Rotation", + "ss.Placement = pl", + "Draft.autogroup(ss)", + "FreeCAD.ActiveDocument.recompute()"] + ) + def reject(self): """Run when clicking the Cancel button.""" self.sourceCmd.finish() - self.platWinDialog("Restore") + self.platform_win_dialog("Restore") return True - def createObject(self): - """Create object in the current document.""" - String = self.form.leString.text().replace('\\', '\\\\').replace('"', '\\"') - String = '"' + String + '"' - FFile = '"' + str(self.fileSpec) + '"' - - Size = str(App.Units.Quantity(self.form.sbHeight.text()).Value) - Tracking = str(0.0) - x = App.Units.Quantity(self.form.sbX.text()).Value - y = App.Units.Quantity(self.form.sbY.text()).Value - z = App.Units.Quantity(self.form.sbZ.text()).Value - ssBase = App.Vector(x, y, z) - try: - Gui.addModule("Draft") - Gui.addModule("WorkingPlane") - self.sourceCmd.commit(translate('draft', 'Create ShapeString'), - ['ss = Draft.make_shapestring(String=' + String + ', FontFile=' + FFile + ', Size=' + Size + ', Tracking=' + Tracking + ')', - 'pl = FreeCAD.Placement()', - 'pl.Base = ' + toString(ssBase), - 'pl.Rotation = WorkingPlane.get_working_plane().get_placement().Rotation', - 'ss.Placement = pl', - 'Draft.autogroup(ss)', - 'FreeCAD.ActiveDocument.recompute()']) - except Exception: - _err("Draft_ShapeString: error delaying commit\n") - class ShapeStringTaskPanelEdit(ShapeStringTaskPanel): """Task panel for Draft ShapeString object in edit mode.""" + def __init__(self, vobj): - base = vobj.Object.Placement.Base - size = vobj.Object.Size.Value - string = vobj.Object.String - font = vobj.Object.FontFile - - super().__init__(base, size, string, font) + self.obj = vobj.Object + super().__init__( + self.obj.Placement.Base, self.obj.Size.Value, self.obj.String, self.obj.FontFile + ) self.pointPicked = True - self.vobj = vobj self.call = Gui.activeView().addEventCallback("SoEvent", self.action) def accept(self): - x = App.Units.Quantity(self.form.sbX.text()).Value - y = App.Units.Quantity(self.form.sbY.text()).Value - z = App.Units.Quantity(self.form.sbZ.text()).Value - - base = App.Vector(x, y, z) + string = self.escape_string(self.form.leString.text()) size = App.Units.Quantity(self.form.sbHeight.text()).Value - string = self.form.leString.text() - font_file = self.fileSpec - o = "FreeCAD.ActiveDocument.getObject(\"" + self.vobj.Object.Name + "\")" - Gui.doCommand(o+".Placement.Base=" + toString(base)) - Gui.doCommand(o+".Size=" + str(size)) - Gui.doCommand(o+".String=\"" + string + "\"") - Gui.doCommand(o+".FontFile=\"" + font_file + "\"") + Gui.doCommand("ss = FreeCAD.ActiveDocument.getObject(\"" + self.obj.Name + "\")") + Gui.doCommand("ss.String=\"" + string + "\"") + Gui.doCommand("ss.FontFile=\"" + self.font_file + "\"") + Gui.doCommand("ss.Size=" + str(size)) + Gui.doCommand("ss.Placement.Base=" + toString(self.point)) Gui.doCommand("FreeCAD.ActiveDocument.recompute()") self.reject() - - return True - - def reject(self): - - self.vobj.Document.resetEdit() - self.platWinDialog("Restore") - return True def finish(self): @@ -221,7 +267,13 @@ class ShapeStringTaskPanelEdit(ShapeStringTaskPanel): Gui.activeView().removeEventCallback("SoEvent", self.call) Gui.Snapper.off() Gui.Control.closeDialog() - return None + def reject(self): + + self.obj.ViewObject.Document.resetEdit() + self.platform_win_dialog("Restore") + return True + + ## @}