Download src/Mod/Draft/draftobjects/label.py from AbdulElahGwaith/FreeCAD: direct link, hf CLI and curl.
- Browser
- Download file 18.4 kB
-
https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Draft/draftobjects/label.py
- Command line
-
hf download hf://AbdulElahGwaith/FreeCAD/src/Mod/Draft/draftobjects/label.py
-
curl -L -o label.py https://huggingface.co/AbdulElahGwaith/FreeCAD/resolve/main/src/Mod/Draft/draftobjects/label.py
18.4 kB
| # SPDX-License-Identifier: LGPL-2.1-or-later | |
| # *************************************************************************** | |
| # * Copyright (c) 2009, 2010 Yorik van Havre <yorik@uncreated.net> * | |
| # * Copyright (c) 2009, 2010 Ken Cline <cline@frii.com> * | |
| # * Copyright (c) 2020 Eliud Cabrera Castillo <e.cabrera-castillo@tum.de> * | |
| # * * | |
| # * This file is part of the FreeCAD CAx development system. * | |
| # * * | |
| # * This program is free software; you can redistribute it and/or modify * | |
| # * it under the terms of the GNU Lesser General Public License (LGPL) * | |
| # * as published by the Free Software Foundation; either version 2 of * | |
| # * the License, or (at your option) any later version. * | |
| # * for detail see the LICENCE text file. * | |
| # * * | |
| # * FreeCAD is distributed in the hope that it will be useful, * | |
| # * but WITHOUT ANY WARRANTY; without even the implied warranty of * | |
| # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * | |
| # * GNU Library General Public License for more details. * | |
| # * * | |
| # * You should have received a copy of the GNU Library General Public * | |
| # * License along with FreeCAD; if not, write to the Free Software * | |
| # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * | |
| # * USA * | |
| # * * | |
| # *************************************************************************** | |
| """Provides the object code for the Label object.""" | |
| ## @package label | |
| # \ingroup draftobjects | |
| # \brief Provides the object code for the Label object. | |
| ## \addtogroup draftobjects | |
| # @{ | |
| from PySide.QtCore import QT_TRANSLATE_NOOP | |
| import FreeCAD as App | |
| from FreeCAD import Units as U | |
| from draftobjects.draft_annotation import DraftAnnotation | |
| from draftutils import gui_utils | |
| from draftutils.messages import _log | |
| from draftutils.translate import translate | |
| class Label(DraftAnnotation): | |
| """The Draft Label object.""" | |
| def __init__(self, obj): | |
| obj.Proxy = self | |
| self.Type = "Label" | |
| self.set_properties(obj) | |
| def set_properties(self, obj): | |
| """Set properties only if they don't exist.""" | |
| self.set_target_properties(obj) | |
| self.set_leader_properties(obj) | |
| self.set_label_properties(obj) | |
| def set_target_properties(self, obj): | |
| """Set position properties only if they don't exist.""" | |
| properties = obj.PropertiesList | |
| if "TargetPoint" not in properties: | |
| _tip = QT_TRANSLATE_NOOP( | |
| "App::Property", | |
| "The position of the tip of the leader " | |
| "line.\n" | |
| "This point can be decorated " | |
| "with an arrow or another symbol.", | |
| ) | |
| obj.addProperty("App::PropertyVector", "TargetPoint", "Target", _tip, locked=True) | |
| obj.TargetPoint = App.Vector(2, -1, 0) | |
| if "Target" not in properties: | |
| _tip = QT_TRANSLATE_NOOP( | |
| "App::Property", | |
| "Object, and optionally subelement, " | |
| "whose properties will be displayed\n" | |
| "as 'Text', depending on 'Label Type'.\n" | |
| "\n" | |
| "'Target' won't be used " | |
| "if 'Label Type' is set to 'Custom'.", | |
| ) | |
| obj.addProperty("App::PropertyLinkSub", "Target", "Target", _tip, locked=True) | |
| obj.Target = None | |
| def set_leader_properties(self, obj): | |
| """Set leader properties only if they don't exist.""" | |
| properties = obj.PropertiesList | |
| if "Points" not in properties: | |
| _tip = QT_TRANSLATE_NOOP( | |
| "App::Property", | |
| "The list of points defining the leader " | |
| "line; normally a list of three points.\n" | |
| "\n" | |
| "The first point should be the position " | |
| "of the text, that is, the 'Placement',\n" | |
| "and the last point should be " | |
| "the tip of the line, that is, " | |
| "the 'Target Point'.\n" | |
| "The middle point is calculated " | |
| "automatically depending on the chosen\n" | |
| "'Straight Direction' " | |
| "and the 'Straight Distance' value " | |
| "and sign.\n" | |
| "\n" | |
| "If 'Straight Direction' is set to " | |
| "'Custom', the 'Points' property\n" | |
| "can be set as a list " | |
| "of arbitrary points.", | |
| ) | |
| obj.addProperty("App::PropertyVectorList", "Points", "Leader", _tip, locked=True) | |
| obj.Points = [] | |
| if "StraightDirection" not in properties: | |
| _tip = QT_TRANSLATE_NOOP( | |
| "App::Property", | |
| "The direction of the straight segment " | |
| "of the leader line.\n" | |
| "\n" | |
| "If 'Custom' is chosen, the points " | |
| "of the leader can be specified by\n" | |
| "assigning a custom list " | |
| "to the 'Points' attribute.", | |
| ) | |
| obj.addProperty( | |
| "App::PropertyEnumeration", "StraightDirection", "Leader", _tip, locked=True | |
| ) | |
| obj.StraightDirection = ["Horizontal", "Vertical", "Custom"] | |
| if "StraightDistance" not in properties: | |
| _tip = QT_TRANSLATE_NOOP( | |
| "App::Property", | |
| "The length of the straight segment " | |
| "of the leader line.\n" | |
| "\n" | |
| "This is an oriented distance; " | |
| "if it is negative, the line will " | |
| "be drawn\n" | |
| "to the left or below the 'Text', " | |
| "otherwise to the right or above it,\n" | |
| "depending on the value of " | |
| "'Straight Direction'.", | |
| ) | |
| obj.addProperty( | |
| "App::PropertyDistance", "StraightDistance", "Leader", _tip, locked=True | |
| ) | |
| obj.StraightDistance = 1 | |
| def set_label_properties(self, obj): | |
| """Set label properties only if they don't exist.""" | |
| properties = obj.PropertiesList | |
| if "Placement" not in properties: | |
| _tip = QT_TRANSLATE_NOOP( | |
| "App::Property", "The placement of the 'Text' element " "in 3D space" | |
| ) | |
| obj.addProperty("App::PropertyPlacement", "Placement", "Label", _tip, locked=True) | |
| obj.Placement = App.Placement() | |
| if "CustomText" not in properties: | |
| _tip = QT_TRANSLATE_NOOP( | |
| "App::Property", "The text to display when 'Label Type' " "is set to 'Custom'" | |
| ) | |
| obj.addProperty("App::PropertyStringList", "CustomText", "Label", _tip, locked=True) | |
| obj.CustomText = "Label" | |
| if "Text" not in properties: | |
| _tip = QT_TRANSLATE_NOOP( | |
| "App::Property", | |
| "The text displayed by this label.\n" | |
| "\n" | |
| "This property is read-only, as the " | |
| "final text depends on 'Label Type',\n" | |
| "and the object defined in 'Target'.\n" | |
| "The 'Custom Text' is displayed only " | |
| "if 'Label Type' is set to 'Custom'.", | |
| ) | |
| obj.addProperty("App::PropertyStringList", "Text", "Label", _tip, locked=True) | |
| obj.setEditorMode("Text", 1) # Read only | |
| # TODO: maybe here we can define a second and third 'label type' | |
| # properties, so that the final displayed text is either | |
| # the first type, or the combination of two or three types, | |
| # if they are available. | |
| # The current system has some labels combined, but these combinations | |
| # are hard coded. By considering multiple properties, we could produce | |
| # arbitrary combinations of labels. | |
| # This would also require updating the `return_info` function | |
| # to handle any combination that we want. | |
| if "LabelType" not in properties: | |
| _tip = QT_TRANSLATE_NOOP( | |
| "App::Property", | |
| "The type of information displayed " | |
| "by this label.\n" | |
| "\n" | |
| "If 'Custom' is chosen, the contents of " | |
| "'Custom Text' will be used.\n" | |
| "For other types, the string will be " | |
| "calculated automatically from the " | |
| "object defined in 'Target'.\n" | |
| "'Tag' and 'Material' only work " | |
| "for objects that have these properties, " | |
| "like BIM objects.\n" | |
| "\n" | |
| "For 'Position', 'Length', and 'Area' " | |
| "these properties will be extracted " | |
| "from the main object in 'Target',\n" | |
| "or from the subelement " | |
| "'VertexN', 'EdgeN', or 'FaceN', " | |
| "respectively, if it is specified.", | |
| ) | |
| obj.addProperty("App::PropertyEnumeration", "LabelType", "Label", _tip, locked=True) | |
| obj.LabelType = get_label_types() | |
| def onDocumentRestored(self, obj): | |
| """Execute code when the document is restored.""" | |
| super().onDocumentRestored(obj) | |
| gui_utils.restore_view_object(obj, vp_module="view_label", vp_class="ViewProviderLabel") | |
| vobj = getattr(obj, "ViewObject", None) | |
| if vobj is None: | |
| return | |
| if not hasattr(vobj, "FontName") or not hasattr(vobj, "FontSize"): | |
| self.update_properties_0v21(obj, vobj) | |
| def loads(self, state): | |
| self.Type = "Label" | |
| def update_properties_0v21(self, obj, vobj): | |
| """Update view properties.""" | |
| old_fontname = vobj.TextFont | |
| old_fontsize = vobj.TextSize | |
| vobj.removeProperty("TextFont") | |
| vobj.removeProperty("TextSize") | |
| vobj.Proxy.set_text_properties(vobj, vobj.PropertiesList) | |
| vobj.FontName = old_fontname | |
| vobj.FontSize = old_fontsize | |
| # The DisplayMode is updated automatically but the new values are | |
| # switched: "2D text" becomes "World" and "3D text" becomes "Screen". | |
| # It should be the other way around: | |
| vobj.DisplayMode = "World" if vobj.DisplayMode == "Screen" else "Screen" | |
| _log("v0.21, " + obj.Name + ", renamed view property 'TextFont' to 'FontName'") | |
| _log("v0.21, " + obj.Name + ", renamed view property 'TextSize' to 'FontSize'") | |
| _log("v0.21, " + obj.Name + ", renamed 'DisplayMode' options to 'World/Screen'") | |
| def onChanged(self, obj, prop): | |
| """Execute when a property is changed.""" | |
| self.show_and_hide(obj, prop) | |
| def show_and_hide(self, obj, prop): | |
| """Show and hide the properties depending on the touched property.""" | |
| # The minus sign removes the Hidden property (show) | |
| if prop == "LabelType": | |
| if obj.LabelType != "Custom": | |
| obj.setPropertyStatus("CustomText", "Hidden") | |
| obj.setPropertyStatus("Target", "-Hidden") | |
| else: | |
| obj.setPropertyStatus("CustomText", "-Hidden") | |
| obj.setPropertyStatus("Target", "Hidden") | |
| def execute(self, obj): | |
| """Execute when the object is created or recomputed.""" | |
| if obj.StraightDirection != "Custom": | |
| p1 = obj.Placement.Base | |
| if obj.StraightDirection == "Horizontal": | |
| p2 = App.Vector(obj.StraightDistance.Value, 0, 0) | |
| elif obj.StraightDirection == "Vertical": | |
| p2 = App.Vector(0, obj.StraightDistance.Value, 0) | |
| p2 = obj.Placement.multVec(p2) | |
| # p3 = obj.Placement.multVec(obj.TargetPoint) | |
| p3 = obj.TargetPoint | |
| obj.Points = [p1, p2, p3] | |
| else: | |
| # If StraightDirection is 'Custom' | |
| # we can draw the leader line manually by specifying | |
| # any number of vectors in the Points property. | |
| # The first point should indicate the position of the text label, | |
| # while the last one should be 'TargetPoint' | |
| # obj.Points = [p1, p2, p3, p4, ...] | |
| # | |
| # The drawing of the line is done in the viewprovider | |
| # | |
| # However, as soon as StraightDirection is changed to | |
| # 'Horizontal' or 'Vertical' this custom list of points | |
| # will be overwritten | |
| pass | |
| if obj.LabelType == "Custom": | |
| if obj.CustomText: | |
| obj.Text = obj.CustomText | |
| elif obj.Target and obj.Target[0]: | |
| target = obj.Target[0] | |
| sub_list = obj.Target[1] | |
| typ = obj.LabelType | |
| # The sublist may be empty so we test it first | |
| subelement = sub_list[0] if sub_list else None | |
| obj.Text = return_info(target, typ, subelement) | |
| else: | |
| obj.Text = [translate("draft", "No Target")] | |
| # Alias for compatibility with v0.18 and earlier | |
| DraftLabel = Label | |
| def get_label_types(): | |
| return [ | |
| QT_TRANSLATE_NOOP("Draft", "Custom"), | |
| QT_TRANSLATE_NOOP("Draft", "Name"), | |
| QT_TRANSLATE_NOOP("Draft", "Label"), | |
| QT_TRANSLATE_NOOP("Draft", "Position"), | |
| QT_TRANSLATE_NOOP("Draft", "Length"), | |
| QT_TRANSLATE_NOOP("Draft", "Area"), | |
| QT_TRANSLATE_NOOP("Draft", "Volume"), | |
| QT_TRANSLATE_NOOP("Draft", "Tag"), | |
| QT_TRANSLATE_NOOP("Draft", "Material"), | |
| QT_TRANSLATE_NOOP("Draft", "Label + Position"), | |
| QT_TRANSLATE_NOOP("Draft", "Label + Length"), | |
| QT_TRANSLATE_NOOP("Draft", "Label + Area"), | |
| QT_TRANSLATE_NOOP("Draft", "Label + Volume"), | |
| QT_TRANSLATE_NOOP("Draft", "Label + Material"), | |
| ] | |
| def return_info(target, typ, subelement=None): | |
| """Return the text list from the target and the given type. | |
| Parameters | |
| ---------- | |
| target: Part::Feature | |
| The object targeted by the label. | |
| typ: str | |
| It is the type of information that we want to extract. | |
| subelement: str, optional | |
| A string indicating a subelement of the `target`; | |
| it could be `'VertexN'`, `'EdgeN'`, or `'FaceN'`, | |
| where `'N'` is a number that starts from `1` up to the maximum | |
| number of subelements in that target. | |
| """ | |
| # print(obj, target, typ, subelement) | |
| if typ == "Name": | |
| return _get_name(target) | |
| if typ == "Label": | |
| return _get_label(target) | |
| if typ == "Tag": | |
| return _get_tag(target) | |
| if typ == "Material": | |
| return _get_material(target) | |
| if typ == "Label + Material": | |
| return _get_label(target) + _get_material(target) | |
| if typ == "Position": | |
| return _get_position(target, subelement) | |
| if typ == "Label + Position": | |
| return _get_label(target) + _get_position(target, subelement) | |
| if typ == "Length": | |
| return _get_length(target, subelement) | |
| if typ == "Label + Length": | |
| return _get_label(target) + _get_length(target, subelement) | |
| if typ == "Area": | |
| return _get_area(target, subelement) | |
| if typ == "Label + Area": | |
| return _get_label(target) + _get_area(target, subelement) | |
| if typ == "Volume": | |
| return _get_volume(target, subelement) | |
| if typ == "Label + Volume": | |
| return _get_label(target) + _get_volume(target, subelement) | |
| return [translate("draft", "Invalid label type")] | |
| def _get_name(target): | |
| return [target.Name] | |
| def _get_label(target): | |
| return [target.Label] | |
| def _get_tag(target): | |
| if hasattr(target, "Tag"): | |
| return [target.Tag] | |
| else: | |
| return [translate("draft", "Tag not available for object")] | |
| def _get_material(target): | |
| if hasattr(target, "Material") and hasattr(target.Material, "Label"): | |
| return [target.Material.Label] | |
| else: | |
| return [translate("draft", "Material not available for object")] | |
| def _get_position(target, subelement): | |
| point = None | |
| if subelement is not None: | |
| if "Vertex" in subelement: | |
| point = target.Shape.Vertexes[int(subelement[6:]) - 1].Point | |
| else: | |
| point = target.Placement.Base | |
| if point is None: | |
| return [translate("draft", "Position not available for (sub)object")] | |
| return [U.Quantity(x, U.Length).UserString for x in tuple(point)] | |
| def _get_length(target, subelement): | |
| length = None | |
| if subelement is not None: | |
| if "Edge" in subelement: | |
| length = target.Shape.Edges[int(subelement[4:]) - 1].Length | |
| elif "Face" in subelement: | |
| length = target.Shape.Faces[int(subelement[4:]) - 1].Length | |
| elif hasattr(target, "Length"): | |
| length = target.Length | |
| elif hasattr(target, "Shape") and hasattr(target.Shape, "Length"): | |
| length = target.Shape.Length | |
| if length is None: | |
| return [translate("draft", "Length not available for (sub)object")] | |
| return [U.Quantity(length, U.Length).UserString] | |
| def _get_area(target, subelement): | |
| area = None | |
| if subelement is not None: | |
| if "Face" in subelement: | |
| area = target.Shape.Faces[int(subelement[4:]) - 1].Area | |
| elif hasattr(target, "Area"): | |
| area = target.Area | |
| elif hasattr(target, "Shape") and hasattr(target.Shape, "Area"): | |
| area = target.Shape.Area | |
| if area is None: | |
| return [translate("draft", "Area not available for (sub)object")] | |
| return [U.Quantity(area, U.Area).UserString.replace("^2", "²")] | |
| def _get_volume(target, subelement): | |
| volume = None | |
| if subelement is not None: | |
| pass | |
| elif hasattr(target, "Volume"): | |
| volume = target.Volume | |
| elif hasattr(target, "Shape") and hasattr(target.Shape, "Volume"): | |
| volume = target.Shape.Volume | |
| if volume is None: | |
| return [translate("draft", "Volume not available for (sub)object")] | |
| return [U.Quantity(volume, U.Volume).UserString.replace("^3", "³")] | |
| ## @} | |